Infrastructure
Cache Invalidation Playbook
By Journal
Caching makes products feel fast, but stale data can quickly erode trust. This sample post covers a practical cache invalidation pattern for product surfaces that combine user settings, workspace data, and computed summaries.
Start with explicit keys
Cache keys should encode the minimum identity needed to retrieve the value safely.
const cacheKeys = { workspaceHome: (workspaceId: string) => `workspace:${workspaceId}:home:v3`, projectSummary: (projectId: string) => `project:${projectId}:summary:v2`, memberProfile: (memberId: string) => `member:${memberId}:profile:v1`,};Avoid keys that depend on unordered objects or hidden defaults. If two callers can describe the same resource differently, they can accidentally create duplicate cache entries.
Freshness policy
| Surface | Time to live | Invalidation source | Notes |
|---|---|---|---|
| Workspace home | 60 seconds | Workspace event bus | User-visible and frequently updated. |
| Project summary | 5 minutes | Project update event | Computed from multiple entities. |
| Member profile | 1 hour | Member settings write | Rarely changes after onboarding. |
Invalidation event
{ "type": "cache.invalidate", "keys": ["workspace:ws_123:home:v3"], "reason": "task.created", "emittedAt": "2026-06-09T14:20:00.000Z"}Handler example
async function handleInvalidation(event: CacheInvalidationEvent) { await Promise.all( event.keys.map((key) => cache.delete(key)), );
metrics.increment('cache.invalidation.completed', { reason: event.reason, keyCount: event.keys.length, });}Common failure modes
- Missing fan-out: one write affects several cached surfaces, but only one key is deleted.
- Over-broad deletes: invalidating an entire workspace after every small edit creates avoidable load.
- Hidden version drift: changing the response shape without changing the key version serves old data to new code.
- No observability: stale reads are reported by users before the system notices.
Review checklist
- Does every cached response have a documented owner?
- Does the key include a version suffix?
- Are invalidation events emitted in the same transaction as the write or immediately after it?
- Is there a safe fallback when the cache layer is unavailable?
The best cache invalidation strategy is boring: small keys, clear ownership, observable deletes, and explicit freshness expectations.
Use caching to reduce repeated work, not to hide uncertainty. When the product requires strict freshness, skip the cache or make the stale window visible in the interface.