Stop 11 · CachingTopics · 1/8

Caching Fundamentals

Part of the Backend Developer Roadmap

In short

The fastest database query is the one you never run. Caching keeps a copy of answers you've already worked out, so the next person gets them in a blink.

Storing the result of an expensive operation so future requests can reuse it instead of recomputing — one of the most effective and most misused performance techniques in backend systems.

The blob character grabs a biscuit packet from a shelf beside the counter, with sacks in a storeroom behind.

Think of it like this

The kirana shop counter

A kirana uncle keeps biscuits, Maggi and matchboxes on the shelf right beside him, and the rice sacks in the godown at the back. Popular things are within arm's reach; everything else is a walk away. A cache is that front shelf: small, fast, and stocked with what's asked for most.

Watch it happen

GET /products/42, thousands of times an hour

No cache

  • Every request runs the same SQL query
  • Database load grows with every visitor
  • Pages slow down when traffic spikes

With cache

  • The first request fills the cache
  • Repeat requests read from memory
  • Database sees a small share of traffic

Cache data that's read often and changes rarely. Decide up front how stale a cached copy is allowed to get.

The big ideas

What you need to know

Hit or miss

A hit means the answer was already in the cache. A miss means you fetch it from the source and usually store it.

Every copy can go stale

Cached data is a snapshot. A TTL (time to live) says how long that snapshot may be trusted.

Caches stack up

Browser, CDN, app memory, Redis, then the database. Each layer catches requests before the next one.

Space is limited

When a cache fills up, an eviction policy like LRU (least recently used) throws out old entries.

See it in code

Walk through the code

ts
1const cache = new Map<string, { value: unknown; expires: number }>();2let hits = 0, misses = 0;3 4export async function cached<T>(key: string, ttlMs: number, load: () => Promise<T>): Promise<T> {5  const entry = cache.get(key);6  if (entry && entry.expires > Date.now()) {7    hits++;8    return entry.value as T;9  }10  misses++;11  const value = await load();12  cache.set(key, { value, expires: Date.now() + ttlMs });13  return value;14}15 16// const product = await cached("product:42", 60_000, () => db.products.find(42));

1/3Look for a fresh entry. If it hasn't expired, that's a hit and the database is skipped.

hits / (hits + misses) is your hit ratio. If it stays low, you're caching the wrong things or the TTL is too short.

Save yourself the pain

Common mistakes

Oops

Caching everything, including data that changes every second.

Do this instead

Cache what's read often and changes rarely. Fast-changing data gives low hit rates and stale answers.

Oops

Adding a cache without deciding how it gets refreshed.

Do this instead

Before writing code, decide the TTL and what happens when the source data changes.

Your mission

Measure a cache

~20 min

Quick check

A product page is cached for 5 minutes. The price changes at minute 1. What do users see until minute 5?

Go deeper

  1. 1Identify one expensive operation in a project that's a good caching candidate.
  2. 2Measure response time with and without caching for the same endpoint.
  3. 3Learn the golden rule: cache invalidation is the hard part, not the caching itself.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge