What Are You Delegating?
Every abstraction is a trade: it removes work by making decisions for you. Those decisions are still being made — just not by you, and not visibly. For each abstraction: what it genuinely handles, what remains yours, and the escape hatch.
An ORMAn HTTP client or SDKA managed database or cloud platformAn identity providerAn LLM or coding agentA web frameworkA library functionA managed message queueA cacheA container runtime and orchestrator
What are you delegating to a cache?
ArchitectureYou write
1const product = await cache.wrap(`product:${id}`, () => db.products.find(id), { ttl: 300 })The abstraction handles
- ✓Sub-millisecond reads for hot keys
- ✓Absorbing repeated reads so the database sees a fraction of the traffic
- ✓Eviction when memory fills
- ✓TTL expiry
Still your responsibility
- →Invalidation — the cache cannot know a row changed unless you tell it, or wait for the TTL
- →Staleness — every cached read is a decision to tolerate an old value for up to
ttlseconds - →Stampedes — keys that expire together recompute together; jitter and single-flight are yours
- →Hot keys and cold starts — one key can pin one node; a restart sends everything to the database at once
- →What happens when the cache is down — cache-aside falls back to the database, which may not survive the fallback
Know your escape hatch
When: Users see stale data, or the database falls over when the cache restarts.
Drop to: An explicit invalidation rule (delete-on-write or events), TTL jitter, single-flight recompute, and a measured hit rate.
A cache buys latency and charges invalidation. The bill always arrives.
Go deeper on this one: