Caching

All lessons

Learn the standard caching strategies — where the cache sits, when it's written, and how stale data gets cleaned up — so you can justify a caching layer instead of just naming Redis.

Problem it solves

Databases are slow relative to how often the same data is re-read. A cache trades a small amount of staleness risk for a large reduction in read latency and database load — but only if you're explicit about who writes to the cache, when, and how invalidation happens. Naming "add a cache" without that detail doesn't answer the interviewer's actual question.

When not to use it

  • Write-heavy, read-rarely data — the cache would be evicted before it's ever read again, wasting memory.
  • Strongly consistent data where even brief staleness is unacceptable (e.g. an account balance mid-transaction) unless paired with careful invalidation.
  • Data with no locality/skew — if every read is for a different key, a cache's hit rate stays near zero.

Failure modes to call out

  • Cache stampede: a hot key expires and thousands of concurrent requests all miss and hit the DB at once — mitigate with request coalescing or jittered TTLs.
  • Stale reads after a write, when invalidation is forgotten on one code path but not another.
  • Unbounded cache growth without an eviction policy (LRU/LFU), leading to memory pressure.
  • Caching per-user data behind a shared cache key, leaking one user's data to another.

Alternatives & trade-offs

Alternatives to Caching and their trade-offs
AlternativeTrade-off
Cache-aside (lazy load)App checks cache, falls back to DB and populates cache on a miss. Simple and cache stays populated with only what's actually read, but the first read after eviction is slow and a stampede can hit the DB hard.
Write-throughEvery write goes to the cache and the DB together, so reads through that same path stay fresh under normal operation. It doesn't guarantee zero staleness — a partial write failure, a concurrent writer that bypasses the cache, or a replica reading stale data can still show old values — and every write pays the cache-write cost, with an unread key still occupying cache space.
Write-back (write-behind)Writes hit the cache and are flushed to the DB asynchronously. Fastest writes, but risks data loss if the cache fails before the flush.
TTL-based expirationSimplest invalidation — no explicit invalidation code, but you're trading a bounded staleness window for that simplicity.

Diagram

Cache-aside read path: check the cache first, only fall back to the database on a miss.

First reader after eviction pays the full DB latency.1. GET key2. miss → read → 3. populateclientcacheDB
Step 1: Cache miss — Request checks the cache, finds nothing, reads from the database, then populates the cache for next time.
Every subsequent reader is fast until the key is evicted or invalidated.GET key → hitclientcacheDB
Step 2: Cache hit — The next request for the same key is served entirely from the cache — the database is never touched.

Scroll the diagrams sideways to see all of them.

Estimation example

A product page read 5,000 times/sec but updated only once every few minutes is an extreme caching win: cache-aside with a short TTL (a few seconds) turns nearly all of that traffic into cache hits, and even a brief staleness window is invisible to users, cutting database read load by roughly two orders of magnitude.

Say this out loud in the interview

  • "This access pattern is read-heavy with locality, so I'd add a cache-aside layer here — is that the right trade-off given the staleness you can tolerate?"
  • "On a write, do we need to invalidate or update the cache, and on which code paths?"
  • "What's our eviction policy, and how do we avoid a stampede when a hot key expires?"

Practice it in a case study

Design a URL Shortener

Redirect lookups are read-heavy and a natural fit for a cache-aside layer in front of the short-code → URL mapping.

Open case study

Done with this lesson?

Mark it complete once you've worked through the content and practice above.

Next lesson

Queues & Streams