← Stackzilla Blog
Cache Invalidation Starts With Deciding How Stale Is Acceptable
Published October 29, 2026
· 3 min read
· caching, Redis, architecture, performance
Choose cache behavior around data freshness, tenant boundaries, and failure recovery instead of adding a cache before defining its contract.
A cache makes an application faster by answering from a saved result instead of repeating work. It also creates another copy of information that can disagree with the source. Before selecting a cache product, decide what that disagreement is allowed to mean.
A public product description might tolerate a short delay. Account permissions, available inventory, or a user's private messages may require different treatment. One expiration policy rarely fits every kind of data.
## Define the freshness contract first
Write down the source of truth, acceptable staleness, invalidation triggers, and behavior when the cache is unavailable. If nobody can explain those four things, the cache is not fully designed.
Time-to-live expiration bounds how long a value can remain cached, but it does not guarantee immediate freshness after a write. Explicit invalidation can reduce that delay, although messages can fail and race conditions still exist.
For example, a reader can fetch an old database value, a writer can update the database and invalidate the cache, and the original reader can then repopulate the cache with its old result. Versioning, carefully chosen ordering, or a different caching strategy may be needed for strict guarantees.
## Make cache keys match the result
A key must include every input that changes the response: account scope, filters, page position, language, and sometimes permission context. Omitting a tenant identifier is not merely a performance bug; it can expose another customer's data.
Version keys when the cached response shape changes. Otherwise a new application version may deserialize data written by an older version and fail in surprising ways.
Negative caching can reduce repeated work for missing records, but choose a short and intentional lifetime. A cached absence can hide a newly created item. Never treat an authorization failure as a globally reusable result.
## Plan for misses and failures
Popular keys can expire together and cause a burst of database traffic. Request coalescing, expiration jitter, or serving an acceptable stale value while one worker refreshes it can reduce that stampede.
Decide whether the application can safely continue without the cache. A performance cache should not accidentally become an unacknowledged source of truth. Capacity-test the underlying service before assuming every miss can fall through.
Measure hit rate alongside latency, database load, stale responses, and errors. A high hit rate is not a success if it returns the wrong account's data or outdated permissions.
Caching works best when it implements a clear data contract. Faster responses are useful only when the application remains correct enough for the action the user is taking.
## Further reading
- [AWS: Caching challenges and strategies](https://aws.amazon.com/builders-library/caching-challenges-and-strategies/)
- [MDN: HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
Read the full article on Stackzilla →