Skip to content
theakrista.dev
Engineering

Caching

Redis on an insurance platform, CDN and render caching on Vercel, and the discipline of cache keys, TTLs, and invalidation. Where caching pays for itself and where it quietly lies to you.

2 min read

Caching has a reputation as a performance tool. In practice I treat it as a correctness budget: every cache is a place where the system is allowed to be wrong, briefly, in exchange for speed. The engineering work is deciding where you can afford to be wrong, for how long, and how you find out.

On a global insurer's platform I ran Redis in front of MySQL-backed REST APIs. On the real-estate marketplace I lean on CDN and server-render caching. On the eSIM platform, caching was one of the levers behind an 80% infrastructure cost cut. Different layers, same three questions.

The layer map

The rule I apply: cache as close to the user as the data's tolerance for staleness allows. A property listing page tolerates 60 seconds of staleness, so cache the whole render at the edge. An insurance policy status tolerates almost none: Redis with explicit invalidation, or no cache at all.

Keys are API design

Cache bugs are usually key bugs. The key must encode everything the response depends on: locale, currency, user tier, API version. Miss one dimension and users see each other's data. That's not a performance bug, it's an incident.

a key that tells the truth
// BAD: cache.get(`property:${id}`)
// same key for JA/EN render, logged-in/out, mobile/desktop
 
const key = [
  "property",
  id,
  locale,
  viewer.tier,        // pricing differs by tier
  "v3",               // schema version, bump to mass-invalidate
].join(":");

That trailing v3 is the cheapest invalidation strategy that exists. When the shape of the cached value changes, bump the version and let the old entries age out. No scan, no flush, no 3am FLUSHALL regret.

Invalidation: prefer TTL, earn event-driven

Everyone quotes the "two hard problems" joke. The practical version is a hierarchy:

  1. Short TTL. The default. Self-healing, no code paths to forget.
  2. TTL + explicit delete on write. For data users edit and immediately re-read.
  3. Event-driven invalidation. Powerful, and now your cache consistency depends on your event delivery. Only when 1 and 2 measurably fail.

One more habit from the SSR bottleneck work on the marketplace: measure the hit rate and alert on it. A cache that silently degrades to a 40% hit rate doesn't fail loudly. The database just gets slower until an unrelated deploy tips it over. The Lighthouse CI checks I added to that pipeline exist for the same reason: performance regressions should fail a build, not wait to be noticed.

Next topic

CI/CD & Release Engineering