Skip to content
Community content. Review instructions before giving them to an AI agent — treat modules like open-source code.

Caching Strategy Rules

Apply a disciplined caching strategy: cache-aside as the default pattern, TTLs on everything, explicit invalidation on writes, stampede protection, versioned cache keys, what to never cache, and measuring hit rates.

Mby @markdownersPublished August 21, 2026 · ~3 min read

0 downloads · Used by 0 stacks

Every cache is a second copy of the truth that can drift from the first — default to cache-aside with an explicit invalidation path for every write, and treat "how does this entry get removed or refreshed" as a mandatory design question for every cache you add, not an afterthought.

Default pattern

  • Use cache-aside (read: check cache, on miss read the source and populate the cache; write: write the source, then invalidate or update the cache) as the default pattern — it's the simplest to reason about and keeps the cache strictly a derived, disposable copy of the source of truth.
  • Never make the cache the only copy of data that matters — a cache must always be safely droppable and rebuildable from the source without data loss; if losing the cache would lose real data, it isn't a cache, it's a database with an availability problem.

TTL on everything

  • Set an explicit TTL on every cache entry, even ones you believe will be actively invalidated on every relevant write — invalidation logic has bugs, deploys miss an invalidation path, and a TTL is the backstop that guarantees staleness is bounded even when explicit invalidation fails.
  • Choose the TTL based on how stale the data is allowed to be for its actual use case, not a single global default — a public content page can tolerate minutes of staleness; a per-user permission check usually cannot.

Invalidation

  • Invalidate (or update) the relevant cache entries synchronously as part of the write path, in the same request/transaction where practical — invalidation that happens "eventually" via a separate async process reintroduces the staleness window you were trying to avoid.
  • Invalidate by the same key structure used to populate the cache — if reads key by user:{id}:profile, the write path for that user must invalidate exactly that key (or a superset), not a guess at what might be cached.
  • When one write affects many cache entries (a tag or category change affecting every item in it), use a versioned or grouped invalidation strategy (a version number embedded in the key, a tag-based cache) rather than trying to enumerate every affected key by hand.

Stampede protection

  • Protect hot keys from cache stampede (many concurrent requests missing the same expired key and all hitting the source simultaneously) with a lock, request coalescing, or a "probabilistic early expiration" that refreshes before the TTL fully expires — an unprotected stampede can take down the source the cache exists to protect.
  • Prefer serving slightly stale data for a brief window while one request refreshes the cache, over letting every concurrent request hit the source — staleness is usually cheaper than a thundering-herd outage.

Cache keys

  • Include a version segment in cache keys tied to the data shape or serialization format (v2:user:{id}) — this lets a deploy that changes what's cached invalidate everything at once by bumping the version, instead of needing a flush-everything migration step.
  • Make keys deterministic and fully derived from their inputs (including any relevant filter/sort/locale parameters) — two logically different requests must never collide on the same key, and the same logical request must always produce the same key.

What never to cache

  • Never cache data that's used for authorization or security decisions (permission checks, entitlements) unless the invalidation path is airtight and the TTL is very short — a stale "yes you're allowed" is a security bug, not a performance tradeoff.
  • Never cache highly personalized responses under a shared key — caching a per-user response under a key that doesn't include the user id serves one user's private data to another.

Measuring effectiveness

  • Track cache hit rate per cache/key-pattern, not just in aggregate — a cache that's globally 90% hit rate can still be nearly useless for the one hot path that actually needed it if that path's own hit rate is low.
  • Revisit or remove caches whose hit rate doesn't justify their invalidation complexity — a cache that's rarely hit but frequently invalidated is net-negative: all the correctness risk, none of the performance benefit.
Badge

Link back to this module from your own README.

Get it on Markdowners
[![Get it on Markdowners](https://markdowners.com/mdstack-badge.svg)](https://markdowners.com/m/markdowners/caching-strategy-rules)

Comments (0)

Sign in to comment. Sign in

No comments yet. Be the first to add one.

Discussions about this module

No discussions about this module yet.

Start a discussion