# Caching Strategies Explained: Cache-Aside vs Write-Through vs Write-Back

Add a cache is the most common answer in a system design interview. It is also the answer that gets the most follow-up questions. Where does the data go first? What happens when the cache and the database disagree? What if the cache node dies mid-write?

If you can only say "we'll use Redis", you will get stuck on the first follow-up. This post breaks down the three caching strategies you need to know for [**caching strategies in system design interviews**](https://scaledojo.dev/hld/learn/caching-for-speed/caching-strategies?utm_source=chatgpt.com), with a short code example for each and a simple way to pick between them.

## Why the strategy matters more than the tool

Redis and Memcached are tools. A caching strategy is the rule that decides *who* reads and writes the cache and the database, and in what order. Two teams can use the same Redis cluster and get completely different consistency and performance, just because of this rule.

## 1\. Cache-Aside (Lazy Loading)

The application owns the logic. It checks the cache first. On a miss, it reads from the database, then fills the cache.

```python
def get_user(user_id):
    key = f"user:{user_id}"
    user = cache.get(key)
    if user:                      # cache hit
        return user

    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    cache.set(key, user, ttl=300) # fill the cache for next time
    return user

def update_user(user_id, data):
    db.update("users", user_id, data)
    cache.delete(f"user:{user_id}")  # invalidate, don't update
```

**Good for:** read-heavy workloads such as product pages, profiles and feeds.

**Watch out for:** the first request after a miss is slow, and there is a small window where stale data can be served. Deleting the key on write (instead of updating it) avoids most race conditions.

## 2\. Write-Through

Every write goes to the cache and the database together, as one operation. The cache is always up to date.

```python
def update_profile(user_id, data):
    db.update("users", user_id, data)           # 1. persist
    cache.set(f"user:{user_id}", data, ttl=300) # 2. keep cache in sync
    return data
```

**Good for:** data that is read right after it is written, where stale reads are not acceptable (account settings, inventory counts).

**Watch out for:** writes are slower because you wait for both. You may also cache data that is never read again, which wastes memory.

## 3\. Write-Back (Write-Behind)

The application writes only to the cache. The cache flushes to the database later, in batches or after a delay.

```python
def record_view(video_id):
    cache.incr(f"views:{video_id}")   # fast, in-memory

# background job, runs every few seconds
def flush_views():
    for key in cache.scan("views:*"):
        count = cache.getdel(key)
        db.execute(
            "UPDATE videos SET views = views + %s WHERE id = %s",
            count,
            key.split(":")[1]
        )
```

**Good for:** write-heavy data where small loss is acceptable, such as view counters, likes and analytics events.

**Watch out for:** if the cache node crashes before the flush, you lose data. Never use this for payments or orders unless the cache is replicated and persistent.

## Quick comparison

| Strategy | Read speed | Write speed | Consistency | Data loss risk |
| --- | --- | --- | --- | --- |
| Cache-aside | Fast after warm-up | Normal | Eventual | Low |
| Write-through | Fast | Slower | Strong | Low |
| Write-back | Fast | Very fast | Eventual | Higher |

## How to pick one in an interview

- **Mostly reads, some staleness is fine?** Cache-aside.
- **Must read your own writes immediately?** Write-through.
- **Huge write volume, can tolerate small loss?** Write-back.

Then say out loud what you are trading away. Interviewers reward that more than the choice itself.

## Don't forget eviction and TTL

Whatever strategy you choose, the cache has limited memory. Mention an eviction policy (LRU is the usual default) and a TTL so stale entries expire on their own. These two lines often separate a good answer from an average one.

## Practice it, don't just read it

Reading about caching is the easy part. The real test is designing it under time pressure and defending your choices. If you want [system design practice](https://scaledojo.dev/) with hands-on labs instead of passive reading, ScaleDojo has 220+ labs covering HLD, LLD, API design and GenAI/RAG systems, so you can try these trade-offs on real problems.

**Key takeaway:** the strategy decides your consistency, speed and failure behaviour. Pick it based on the read/write pattern, and always state the trade-off.
