Mert Tosun
← Posts
Caching Patterns: Cache-Aside, Write-Through, and Write-Behind

Caching Patterns: Cache-Aside, Write-Through, and Write-Behind

Blog Author4 min readBackend

Caching is one of the highest-leverage tools in backend engineering, yet it is also one of the easiest places to create subtle consistency bugs. Teams often add Redis, set a TTL, and expect immediate performance gains. Sometimes that works. More often, edge cases emerge: stale reads after writes, cache stampede under load, partial invalidation, and hard-to-debug divergence between source of truth and cached state.

Choosing a caching strategy should be an explicit architectural decision. Cache-Aside, Write-Through, and Write-Behind each optimize different dimensions: read latency, write durability, operational simplicity, and consistency guarantees. Understanding their trade-offs helps prevent expensive rework later.

Cache-Aside pattern

Cache-Aside (lazy loading) is the most common pattern. The application first checks cache; on miss, it reads from database, stores the result in cache, and returns it. Writes typically update database first and then invalidate or refresh the cache key.

Why teams like it:

  • Simple to adopt incrementally.
  • Cache is only populated for hot keys.
  • Minimal coupling between cache and database layers.

Where it fails:

  • Stale data when invalidation is missed.
  • Stampede when many requests miss the same key simultaneously.
  • Inconsistent TTL policies across services.

To harden Cache-Aside, add per-key request coalescing, jittered TTLs, and explicit invalidation contracts per domain aggregate.

Write-Through pattern

Write-Through updates cache and database in the write path, keeping cache immediately fresh after successful writes. Reads then hit cache with high probability.

Advantages:

  • Better read consistency right after writes.
  • Predictable hit rates for frequently accessed entities.
  • Easier to reason about freshness for read-heavy APIs.

Trade-offs:

  • Higher write latency because cache write is synchronous.
  • Write amplification when data is rarely read.
  • More coupling in write pipeline and error handling complexity.

Write-Through is strong for systems with strict read-after-write expectations, such as user settings or account state surfaces.

Write-Behind pattern

Write-Behind (write-back) accepts writes into cache first and persists to database asynchronously. This can dramatically reduce perceived write latency and smooth burst traffic, but it introduces risk because source-of-truth persistence is delayed.

Benefits:

  • Very low write latency.
  • Better buffering under bursty workloads.
  • Potentially higher throughput for append-like operations.

Risks:

  • Data loss if cache layer fails before flush.
  • Ordering complexity for dependent writes.
  • Harder recovery semantics after partial failures.

If you use Write-Behind, pair it with durable queues or transaction logs and explicit replay mechanics. Never rely on in-memory buffers alone for business-critical writes.

Consistency and invalidation strategy

Most caching incidents are invalidation incidents. Define who owns key lifecycle and when invalidation happens:

  • On successful write commit?
  • On domain event consumption?
    • On background reconciliation?

Avoid mixing multiple invalidation paths without clear precedence. Event-driven invalidation can scale well, but only if delivery guarantees and idempotency are explicit.

Also treat cache TTL as a risk budget, not a random number. Short TTLs increase load; long TTLs increase staleness. Choose TTL per business tolerance and data volatility.

Preventing stampede and hot key collapse

Under high traffic, cache misses can overload the database. Mitigation techniques include:

  • Single-flight request deduplication.
  • Probabilistic early refresh.
  • Soft TTL with background refresh.
  • Hot-key sharding and local in-process fallback cache.

These controls should be tested in load simulations, not only inferred from staging behavior.

Rollout and observability checklist

Introduce caching in phases:

  1. Instrument baseline latency and DB load.
  2. Enable cache for one endpoint or entity type.
  3. Measure hit ratio, staleness complaints, and error shifts.
  4. Add invalidation and stampede controls.
  5. Expand scope with per-domain runbooks.

Track metrics such as hit rate, miss penalty, stale-read incidents, write propagation lag, and eviction churn. Without these, cache "success" is often assumed rather than proven.

Caching is not just performance optimization; it is consistency engineering. Teams that choose patterns intentionally and monitor real failure modes get durable speed gains without sacrificing correctness.