← back to blog
system design

Caching, Explained
From Cache-Aside to Cache Stampede

A field guide to cache types, placement strategies, write/read patterns, and the failure modes that catch every engineer at least once. Every cache exists to answer one question faster than the source of truth can: "have I seen this before?"

01 Types of Cache

Before picking a pattern, decide where the cache physically lives. That choice alone determines its blast radius.

External Cache

A separate service — think Redis or Memcached — sitting between your app server and your database. The app checks the cache first; if it's empty, it falls back to the database.

Client App Server Cache DB ① cache check ② DB fallback
Client → App Server → (Cache first, then DB on miss)

Client-Side Caching

The cache moves all the way to the edge — into the client itself. Zero network hops on a hit, but you give up control: you can't centrally invalidate a thousand browsers or mobile devices at once.

In-Process Cache

The cache lives inside the application server's own memory (a local hashmap). Fast and simple — but in a multi-server deployment, each instance has its own copy, so consistency across servers isn't guaranteed.

02 CDN — Caching at the Edge

A Content Delivery Network is really just an external cache distributed geographically. Instead of one cache near your database, you have many caches — points of presence (PoPs) — scattered around the world. A client always talks to the nearest one.

This is why a video loads instantly for someone in Tokyo and someone in Toronto at the same time — both are hitting a local edge node, not a single origin server on the other side of the planet.

✦ key insight
CDN is just a geographically distributed external cache. The same cache-miss and invalidation principles apply — they're just harder to control at scale.

03 Read & Write Patterns

Where the cache lives is one decision. How it gets populated and kept in sync with the database is another — and this is where most of the interesting tradeoffs live.

Cache-Aside (Lazy Loading)

The most common pattern. The application owns the fallback logic. On a read, check the cache first. Miss? Read from the database, then write that value into the cache for next time.

App Server Cache DB ① check cache ② read from DB
App owns the logic: check cache → miss → read DB → populate cache

Tradeoff: simple and resilient (a cache outage just means slower reads), but the first request for any key always pays the full database cost.

Write-Through Cache

Writes go to the cache first, and the cache synchronously forwards the write to the database before confirming completion. The cache and DB never drift apart, but every write now pays the latency of both systems.

App Server Cache DB write to cache sync write to DB
Write completes only once the DB write is synced — strong consistency, higher latency

Write-Behind Cache (Write-Back)

The cache acknowledges the write immediately and flushes to the database asynchronously in the background. Writes feel instant, but there's a window where data exists only in the cache — a crash there means data loss.

Read-Through Caching

Similar to cache-aside, but the cache itself owns the fallback logic — the application only ever talks to the cache, and the cache transparently loads from the database on a miss.

04 Comparing the Patterns

PatternBest forRisk
Cache-AsideRead-heavy, simple workloadsCold cache penalty on first read
Write-ThroughStrong cache/DB consistencyHigher write latency
Write-BehindWrite-heavy, latency-sensitive appsPossible data loss on crash
Read-ThroughCentralizing fallback logicTighter coupling to the cache layer

05 Common Problems With Cache

A cache that never fails is a cache you haven't load-tested yet. Here's the failure mode every system eventually meets:

Cache Stampede (Thundering Herd)

When a popular cache key expires — or the cache goes down — every concurrent request gets a miss at the same instant. Instead of one request hitting the database, all of them do, simultaneously, overwhelming it.

Client Client Client Client App Server Cache miss DB overwhelmed
One cache miss, many clients → every request floods straight to the database

This is why production caching systems add jitter to expiry times, use locks or "request coalescing" so only one request rebuilds a hot key, or keep serving slightly stale data while a single background job refreshes it.

06 Key Takeaways

A cache doesn't remove the database — it changes how often you have to ask it the same question.
  • Pick cache placement based on who needs the speed — client-side for zero hops, CDN for geo-distributed reads, external for shared server state
  • Cache-Aside is the safest default — resilient to cache failure, easy to reason about
  • Write-Through when you need strong cache/DB consistency and can tolerate write latency
  • Write-Behind for write-heavy workloads where latency matters more than durability
  • Plan for the cache stampede — add expiry jitter, use request coalescing, or serve stale while refreshing
  • A cache outage should degrade performance, not take down the system — build fallbacks