8 min read

Eye Training · Caching and Memoization

A cache is a copy. Every caching bug is a copy that is wrong, shared, stale, unbounded, or rebuilt by everyone at once.

Part 2 · Surface 17. Bug counts are hidden and some snippets are clean. See Start Here for how to use these drills.


J · The basics

Bug hunt
J · Product listing
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Cache the settings
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Memoize
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
J · Cached user
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min

M · Three years in, these should jump out

Bug hunt
M · Unread count
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Feature flags
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Exchange rates
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Render markdown
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Update an email
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Dashboard
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
M · Load once
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min

S · Where seniors stand out

Bug hunt
S · Homepage
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
S · Profile page
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 1 min
Bug hunt
S · After the deploy
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min
Bug hunt
S · Permissions cache
Bug count hidden, and some snippets are clean. Click every line you think is wrong (or none), then check.
0:00 / 2 min

What your eye checks

Whenever you see a cache, a Map used as one, or a memoize, ask:

  1. Is every input in the key? Page, filters, locale, user, tenant, version. A missing one means wrong data; a missing user means leaked data.
  2. When does it expire, and what clears it on change? No TTL plus no invalidation means stale forever.
  3. Write first, then invalidate. Deleting the key before the write lets a reader cache the old value.
  4. Is the hit check correct for every value? if (cached) treats 0, '' and false as misses.
  5. Can a caller mutate the cached value? Shared objects need copying or freezing.
  6. Are failures cached? A rejected promise or an error result shouldn't stick.
  7. Is it bounded? A Map that only grows is a memory leak with a delay.
  8. What happens when a hot key expires? Stampede: lock, single-flight, serve stale, jitter.
  9. Is it per instance? In-process caches differ across instances and survive role changes; who tells them to forget?
  10. HTTP: public versus private; personal responses must never go to a shared cache.

Tells

You seeAsk
A key built from some of the inputsWhat else changes the result?
A key with no user or tenantWhose data is this?
redis.set(key, value) with no EXWhen does it expire?
del before updateWhat does a reader cache in between?
if (cached)What if the value is 0 or ''?
.push / assignment on something from a cacheShared copy?
cache.set(key, promise)What if it rejects?
new Map() at module level, only setWho removes entries?
A slow build after a missWhat do the other requests do meanwhile?
Cache-Control: public on a logged-in pageWho else gets it?
A shape change and an existing cacheVersioned key?

Go deeper