DEV Community

Cover image for To Cache or Not to Cache: A Practical Decision Tree for Engineers
zaheetdev
zaheetdev

Posted on

To Cache or Not to Cache: A Practical Decision Tree for Engineers

To Cache or Not to Cache: A Practical Decision Tree for Engineers

Caching is one of those tools that can make your system feel magically fast—or spectacularly wrong. The trick isn’t how to cache; it’s when and what to cache. Here’s a concise playbook inspired by the “To Cache or Not to Cache” flow many of us sketch on whiteboards, plus a Mermaid diagram you can drop into docs.


The decision tree in plain English

  1. Is it accessed often?
  • No: Don’t cache. Save the complexity budget.
  • Yes: Continue.
  1. Is it expensive to fetch? (slow query, external API, heavy compute)
  • No: Still don’t cache—your bottleneck isn’t here.
  • Yes: Continue.
  1. How stable is the data?
  • Stable: Great cache candidate.

    • Is it small & simple?
      • No: Consider partial caching (e.g., precomputed aggregates, projections).
      • Yes: Continue.
  • Volatile: Only proceed if you can invalidate reliably.

    • No invalidation: Avoid caching; correctness beats speed.
    • Have invalidation: Use short TTL or event-driven invalidation.
  1. Does it impact UX or critical internal throughput?
  • No: Avoid caching unless it unblocks a heavy internal job.
  • Yes: Continue.
  1. Is it safe to cache? (PII, tenant boundaries, auth scope)
  • No: Use scoped or encrypted keys; strip sensitive fields or cache derived artifacts.
  • Yes: Continue.
  1. Will it scale? (key cardinality, memory/eviction, dogpile risk)
  • No: Redesign—shard, precompute, batch, or add a write-through store.
  • Yes:Cache it.

Patterns that pair well with this tree

  • Cache-aside (lazy): App reads→miss→load source→set cache. Simple, flexible.
  • Write-through: On write, update DB and cache together. Stronger consistency; higher write latency.
  • Write-behind: Buffer writes and update DB asynchronously. Great for throughput; needs durability safeguards.
  • Stale-while-revalidate: Serve slightly stale data immediately, refresh in background. Excellent UX for stable data.
  • Event-driven invalidation: Publish domain events (e.g., product.updated) to evict or refresh keys.

TTL & invalidation quick guide

  • Highly stable content: TTL hours–days, plus manual bust on deploy/version bump.
  • Moderately dynamic lists: TTL minutes; SWR for smooth UX.
  • Volatile counters/prices/stock: Event invalidation or TTL seconds; consider moving the truth to a fast primary (e.g., Redis as source-of-truth with periodic snapshot).
  • Always include a version (e.g., v3:) in keys to invalidate wholesale after schema/logic changes.

Cache key & security tips

  • Scope keys by tenant/user/locale/feature flags: inv:v3:tenant:{id}:list?status=overdue&sort=due_at
  • Never cache raw secrets or PII. Cache IDs or rendered views, not sensitive blobs.
  • For user-specific views, bind to auth scope and role.
  • Consider request coalescing (single-flight) to avoid thundering herds on misses.

Operability checklist

  • Track hit rate, p95 latency, evictions, and origin load.
  • Implement graceful degradation when cache is cold or unavailable.
  • Add circuit breakers around origins and dogpile protection (locks/jitter).
  • Document who owns the invalidation logic.

Cache OR No Cache Decision


Example: applying the tree

  • Product catalog page (read-heavy, DB joins, updates hourly): Cache-aside, TTL 5–10 min, SWR, event bust on product update.
  • User dashboard totals (expensive aggregates, per-user): Precompute to a partial cache (e.g., nightly job + small deltas), scoped keys, TTL 15–30 min.
  • Live stock levels (volatile): Prefer event-driven invalidation or a fast primary store; if caching, TTL seconds with coalesced refresh.

Closing thought

If everything is cached, nothing is reliable. If nothing is cached, nothing is fast. Use the decision tree to protect correctness first, then buy back latency where it matters.


Top comments (0)