12. Caching: The Basics
12.9 When should you cache, what should you never cache, and how do you name keys?
When does caching help, and when doesn't it?
In the kitchen: Keep milk in the fridge because you use it every day, a trip for it is a hassle, and yesterday's milk is still fine. You don't keep a cake you'll eat once, or fish that changes every hour, and when guests ask "is this fresh right now?", you check, you don't guess.
In the system:
Cache it when the data can REST:
- Repeated: the same data is read many times (product pages, profiles, settings).
- Expensive: it's slow or costly to produce (heavy queries, calculations, calls to other services).
- Stale is OK: a few seconds out of date won't hurt anyone.
- Tilted to reads: it's read far more often than it's written.
Don't cache the three Us:
- Unique: every request asks for something different, so nothing is reused.
- Unstable: it changes constantly, so the copy is stale almost instantly.
- Urgent truth: it must be exactly right, right now, like a balance at the moment of payment or the final stock check before an order. Read that from the database.
Memory code: "Cache what can REST. Skip the three Us."
What does a cache cost you?
In the kitchen: A fridge isn't free. It costs money and electricity, it's one more thing that can break, food in it can go stale without you noticing, and someone has to keep it organised.
In the system: A cache costs you MOSS:
- Memory: RAM costs money.
- One more system: something else to run, monitor and fix when it breaks.
- Stale data: users may see old values.
- Spaghetti risk: extra code to check the cache, fill it and clear it. Get it wrong and you get confusing bugs.
The trade: a little freshness and some extra complexity, in exchange for big gains in speed and database load. For read-heavy apps, almost always worth it.
Memory code: "Every cache gathers MOSS." Memory, One more system, Stale data, Spaghetti risk.
How should you name cache keys?
In the kitchen: A label that just says "milk" is fine in one house. In a shared flat, you need "Ravi's milk, full cream, 1 L", or someone drinks the wrong one.
In the system: A key must capture everything the value depends on, and nothing else:
product:milk:v2 the product, with a schema version
user:7:cart per-user data: the user ID is IN the key
search:q=milk&page=2&lang=en every parameter that changes the result
- Missing a part means serving the wrong data: leave the user ID out of a cart key and one user sees another's cart. That's a privacy incident, not just a bug.
- Including needless parts (a timestamp, a random request ID) makes every key unique, so the hit rate falls to zero.
- Add a version (
:v2) so a code change that alters the value's shape can switch to fresh keys instead of reading old, incompatible ones. - Use a consistent prefix per kind of data (
product:,user:) so you can find, measure and clear them by group.
Memory code: "Put everything that changes the answer in the name, and nothing else."