Caching Strategies Every Developer Should Know
There are only two hard things in computer science#
Cache invalidation and naming things. We'll tackle the harder one.
Caching is the single most effective performance optimization in software. A well-placed cache can turn a 200ms database query into a 1ms lookup. But get it wrong and you'll spend days debugging stale data.
The caching decision tree#
Before adding a cache, ask:
- Is this data read more than written? If writes dominate, caching might not help
- Can users tolerate slightly stale data? If not, caching gets complicated
- Is the data expensive to compute or fetch? If it's a simple index lookup, caching adds complexity without much benefit
If the answer to #1 and #3 is yes, cache it.
Cache-aside (lazy loading)#
The most common pattern. The application checks the cache first, falls back to the database on miss.
Read:
1. Check cache → hit? Return cached data
2. Miss → query database
3. Store result in cache
4. Return data
Write:
1. Update database
2. Invalidate cache (delete the key)
Pros: Only caches data that's actually requested. Simple to implement. Cons: First request is always a cache miss. Possible race conditions on concurrent writes.
Use when: Most web applications. User profiles, product pages, API responses.
Write-through#
Every write goes to both the cache and the database simultaneously.
Write:
1. Write to cache AND database together
2. Cache is always up-to-date
Read:
1. Always read from cache (it's guaranteed fresh)
Pros: Cache is never stale. Reads are always fast. Cons: Write latency increases (two writes per operation). Caches data that may never be read.
Use when: Data that's written and immediately read back. Session stores, shopping carts.
Write-behind (write-back)#
Writes go to the cache immediately, then asynchronously flush to the database.
Write:
1. Write to cache (fast, return immediately)
2. Background job writes to database later
Read:
1. Always read from cache
Pros: Fastest writes possible. Can batch database writes for efficiency. Cons: Data loss risk if cache crashes before flushing. Complex to implement correctly.
Use when: High write throughput where you can tolerate some data loss risk. Analytics, view counts, rate limiters.
Eviction policies#
When the cache is full, something has to go:
| Policy | How it works | Best for |
|---|---|---|
| LRU | Evict least recently used | General purpose (most common) |
| LFU | Evict least frequently used | Data with stable popularity |
| TTL | Expire after time limit | Data that becomes stale |
| Random | Evict randomly | When access patterns are uniform |
Default to LRU + TTL. Set a TTL on every cached item — even if it's long (24 hours). This prevents the "forgot to invalidate" class of bugs.
The cache invalidation problem#
The hardest part isn't caching. It's knowing when to invalidate.
Pattern 1: Time-based expiration (TTL) Set it and forget it. Data refreshes every N minutes. Simple but allows stale reads within the TTL window.
Pattern 2: Event-based invalidation When data changes, publish an event. Cache subscribers delete the relevant keys. More complex but eliminates stale reads.
Pattern 3: Version keys
Include a version number in the cache key: user:123:v5. On update, increment the version. Old keys expire naturally.
Common mistakes#
Caching too aggressively. Not everything needs a cache. Adding Redis between your app and a database that handles 100 queries/second adds complexity without meaningful benefit.
Forgetting to invalidate. Users see stale data. Support tickets pile up. You spend hours debugging "why isn't the update showing?"
Cache stampede. Popular key expires → 1000 requests simultaneously hit the database → database crashes. Fix with request coalescing or "stale-while-revalidate."
Caching errors. An API returns a 500, you cache the error response, now every user sees the error for the TTL duration. Always check response validity before caching.
Where caching fits in your architecture#
In a typical system, you'll have multiple cache layers:
- Browser cache — HTTP cache headers, service workers
- CDN cache — static assets, API responses at the edge
- Application cache — Redis/Memcached for computed data
- Database cache — query result cache, connection pooling
Each layer serves a different purpose. The closer to the user, the faster the response.
On Codelit, you can see where caching fits in any architecture. Generate a system and click the cache node — the audit tool tells you if your caching strategy has gaps.
Explore caching in real architectures: describe your system on Codelit.io and see where caching should go.
Try it on Codelit
Chaos Mode
Simulate node failures and watch cascading impact across your architecture
AI Architecture Review
Get an AI audit covering security gaps, bottlenecks, and scaling risks
Related articles
Build this architecture
Generate an interactive architecture for Caching Strategies Every Developer Should Know in seconds.
Try it in Codelit →
Comments