[← diagnostics](11-diagnostics.md) · [README](../README.md) # 12. Before this goes to production ## Should there be a cache here at all? Be honest about the answer. A cache is a correctness liability you accept in exchange for latency. If the method is not measurably slow, or the hit rate will be low, or the data must be current, the right amount of caching is none. Half the caches in a typical codebase were added without a measurement and are never revisited. ## The checklist **Configuration** - [ ] `spring.cache.type` is set explicitly, so a new dependency cannot change the provider ([chapter 8](08-providers-and-ttl.md)) - [ ] `spring.cache.cache-names` declares every cache, so a typo fails loudly - [ ] Every mutable cache has a TTL. It bounds the damage from every other mistake on this list - [ ] Every cache has a size bound, or the data set is provably small - [ ] `@EnableCaching` is not on the main application class **Correctness** - [ ] One cache name per method, or an explicit `key` that includes the method ([chapter 4](04-keys.md)) - [ ] Keys are immutable and serialise to something stable - [ ] Cached values are DTOs, not JPA entities ([chapter 9](09-versus-hibernate-l2.md)) - [ ] Cached values are immutable, or defensively copied — the map hands every caller the same instance - [ ] Mutating methods evict rather than put, with `beforeInvocation = true` ([chapter 5](05-eviction.md)) - [ ] Nothing relies on `this.cachedMethod(...)` ([chapter 3](03-self-invocation.md)) - [ ] Every cached method is called at least once by a test — four of the five invalid declarations in [chapter 2](02-the-three-annotations.md) only fail at the first call **Operations** - [ ] Hit rate and eviction count are on a dashboard (`recordStats()` for Caffeine) - [ ] There is a way to clear a cache without a deployment (`DELETE /actuator/caches/{name}`) - [ ] A `CacheErrorHandler` decision has been made per cache, not inherited by accident ([chapter 5](05-eviction.md)) - [ ] The behaviour with `spring.cache.type=none` has been tried at least once **Distributed caches only** - [ ] Values are serializable and the format survives a rolling deployment — a changed DTO shape with old entries still in Redis fails on read, per instance, at whatever hour - [ ] `spring.cache.redis.key-prefix` keeps this application out of everyone else's keyspace - [ ] The failure mode when the cache is unreachable has been decided: degrade or fail - [ ] TTLs are short enough that a missed eviction self-corrects ## The one-line version Give every cache a TTL, evict rather than put, cache DTOs, and set `spring.cache.type`. Those four cover most of what goes wrong.