A simpler way to think about caching
Most caching bugs aren't caching bugs — they're invalidation bugs wearing a caching costume. The mental model that's held up for me across three very different backends: cache the answer, key it by everything that could change it, and never try to update a cache in place.
In practice that means the key includes every input that affects the value — user id, filters, a version number for the underlying record — and any write that changes the underlying data bumps the version instead of trying to patch the cached copy. Patch-in-place is where every subtle bug I've debugged in this space came from.
It costs a bit more memory, since old versions linger until they expire naturally instead of being cleaned up immediately. That trade has been worth it every time, because the failure mode of over-caching (stale-for-a-few-minutes) is far cheaper than the failure mode of a bad invalidation (wrong data with total confidence).