Core Concepts
Caching
Trading a little staleness for a lot of speed — where caches live, the cache-aside pattern, invalidation strategies, the stampede problem, and deciding what is allowed to be wrong for a while.
A cache keeps a copy of expensive-to-compute data somewhere fast, so most requests skip the expensive work. It's the single highest-leverage performance tool you have — and the single easiest way to serve wrong data. The whole skill is deciding what may be stale, and for how long.
Caches live at every layer
"Add a cache" isn't one decision — there's a cache at nearly every hop between the user and your database. Cheapest wins are highest up:
| Layer | Example | Caches |
|---|---|---|
| Browser | HTTP Cache-Control | Static assets, GET responses |
| CDN | Cloudflare, CloudFront | Images, public pages, API GETs |
| In-memory (app) | A local map | Config, hot lookups per instance |
| Distributed | Redis, Memcached | Shared results across all app servers |
| Database | Query/buffer cache | Hot rows and pages |
Push caching as far up as you can: a response served from a CDN never touches your servers at all.
The cache-aside pattern
The default pattern for application data. Your code owns the logic:
read: check cache → hit? return it
→ miss? read DB, store in cache with a TTL, return it
write: write DB, then delete (or update) the cache keyasync function getProduct(id: string) {
const key = `product:${id}`;
const hit = await cache.get(key);
if (hit) return JSON.parse(hit);
const row = await db.product.findUnique({ where: { id } });
await cache.set(key, JSON.stringify(row), { ex: 300 }); // 5 min TTL
return row;
}Two properties make this safe: a cache miss degrades (you just hit the DB, you don't fail), and a TTL guarantees staleness is bounded even if an invalidation is missed.
Invalidation is the hard part
"There are only two hard things in computer science: cache invalidation and naming things."
Three strategies, usually combined:
- TTL (expiry): entries die after N seconds. Simple; data can be stale up to N.
- Delete-on-write: after any write that affects a key, delete it so the next read recomputes. Fresher, but you must remember every key a write touches.
- Versioned keys: bake a version into the key (
v2:product:{id}) and bump it to discard a whole class of entries at once — handy on deploys or schema changes.
TTL + delete-on-write is the pragmatic default: the delete keeps it fresh, the TTL is a safety net for the invalidation you forgot.
Beware the stampede
When a hot key expires, thousands of concurrent requests all miss at once and hammer the database together — a "cache stampede" that can take the DB down precisely when traffic is highest. Defences:
- Add jitter to TTLs so many keys don't expire on the same second.
- Lock on rebuild: let only the first request recompute a missing hot key while the rest wait briefly and retry.
- Pre-warm critical keys before traffic arrives.
Decide what may be stale — out loud
This is a product decision, not a technical one. Make the list explicit:
| Cache hard (seconds–minutes) | Cache briefly / carefully | Never cache |
|---|---|---|
| Follower counts, like counts | Prices, inventory display | Account balances |
| Public profiles | Search results | Stock at purchase time |
| Homepage / trending feeds | A user's unread count | Auth / permission checks |
The through-line: a number people glance at can be a little wrong; a number people act on cannot. Showing a slightly stale seat map is fine; selling a seat based on a stale map is not.
What to say in an interview
Name the hot read, put a cache in front of it, and state the invalidation and the staleness you're accepting:
"The product page is read thousands of times a minute and changes rarely, so I'll cache it in Redis cache-aside with a 5-minute TTL, and delete the key on any product update. If a key is wrong for a few seconds it's fine — it's display data. I'd never cache the checkout stock count the same way; that reads from the primary inside the purchase transaction."
Next up: Sharding — what to do when the data no longer fits on one machine.
