caching-strategies
Skill8 starsChanged 10 days agoMITView on GitHub
Designs caches that stay correct, covering where to cache (browser, CDN, application, distributed), cache-aside vs write-through, key design, TTLs, invalidation, stampede protection and HTTP Cache-Control headers. Use when adding Redis, Memcached, a CDN or HTTP caching, when users see stale data or cache hit rates are low, or when someone proposes caching a slow endpoint or query. Not for finding why code is slow (see performance-profiling) or tuning queries (see sql-optimization).
11 other projects use a version of this.
Adopted by
Most recently taken up by
- sf-scalar/skiLL.Mdtheir own version, first seen Nov 8, 2026
- cookieMonsterDev/agents-skillstheir own version, first seen Nov 1, 2026
- ThibautBaissac/rails_ai_agentstheir own version, first seen Oct 8, 2026
- novatic-cu/nova-skillstheir own version, first seen Sep 20, 2026
- 2867203296/hermes-agenttheir own version, first seen Aug 19, 2026
- Bappoz/My-Claude-Skillstheir own version, first seen Jul 15, 2026
Other versions11 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 11
- ThibautBaissac/rails_ai_agents★ 665same name
- cookieMonsterDev/agents-skills★ 7same name
- sf-scalar/skiLL.Md★ 6same name
- ffsshhttiikk/opencode-agents-skills★ 2same name
- kimtth/agent-skill-100-lines-or-less★ 2same name
- Bappoz/My-Claude-Skills★ 1same name
- harrissohail6/claude-code-config★ 1same name
- michaelsvanbeek/personal-agent-skills★ 1same name
- novatic-cu/nova-skills★ 1same name
- 2867203296/hermes-agent★ 0same name
1 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository60 other files it gives its agents.
- api-designSkill
- auth-implementationSkill
- background-jobsSkill
- building-agentsSkill
- ci-cd-pipelinesSkill
- cloud-cost-optimizationSkill
- codebase-onboardingSkill
- code-review-changesSkill
- concurrency-patternsSkill
- database-migrationsSkill
- database-schema-designSkill
- database-testingSkill
- data-pipelinesSkill
- data-privacy-retentionSkill
- dependency-upgradesSkill
- design-docsSkill
- design-systemsSkill
- dockerfilesSkill
- domain-driven-designSkill
- event-driven-architectureSkill
- file-storage-uploadsSkill
- frontend-securitySkill
- git-workflowSkill
- graphql-apisSkill
- grpc-servicesSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- internationalizationSkill
- kubernetes-deploymentsSkill
- large-scale-migrationsSkill
- legacy-refactoringSkill
- llm-evalsSkill
- llm-integrationSkill
- mcp-serversSkill
- micro-frontendsSkill
- ml-experimentsSkill
- monorepo-managementSkill
- nosql-designSkill
- observabilitySkill
- orm-patternsSkill
- performance-profilingSkill
- postgres-operationsSkill
- rag-systemsSkill
- react-patternsSkill
- realtime-apisSkill
- resilience-patternsSkill
- root-cause-debuggingSkill
- search-integrationSkill
- security-auditSkill
- seo-and-ssrSkill
- sql-optimizationSkill
- system-designSkill
- test-strategySkill
- threat-modelingSkill
- typescript-patternsSkill
- unit-testingSkill
- verify-before-doneSkill
- web-accessibilitySkill
- web-performanceSkill
- writing-skillsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. 0 are common, 13 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 9 edited versions we read keep these.
13
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Not for: finding the slow code path (performance-profiling) or fixing a slow query (sql-optimization).
- Distinctive: line 23
Characterize the data: Read/write ratio, value size, number of distinct keys, who may see it (public, per tenant, per user), and the …
- Distinctive: line 25
Pick the pattern: Default to cache-aside with a TTL and delete-on-write.
- Distinctive: line 66
Add jitter (for example ±10-20%) so entries written together do not expire together.
- Distinctive: line 67
Use a shorter TTL for negative results ("not found").
- Distinctive: line 88
When a hot key expires, many requests miss at once and all hit the source.
- Distinctive: line 92
Soft and hard TTL (stale-while-revalidate): after the soft TTL, one request refreshes in the background while others get the stale value.
- Distinctive: line 103
Check whether the source survives a 100% miss rate.
- Distinctive: line 105
Never store data only in the cache unless it is deliberately configured as a durable store.
- Very specific: line 120
Always send an explicit Cache-Control.
- Distinctive: line 122
Prefer versioned URLs over CDN purges.
- Distinctive: line 124
Read references/http-caching.md when setting headers, configuring a CDN cache key, or debugging why a response was or was not cached.
- Very specific: line 129
Make sure the CDN never caches responses that carry Set-Cookie or depend on Authorization, and that it honors private and no-store.
- Distinctive: line 143
Updating the cache on write: instead of deleting the key.
- Distinctive: line 155
Every entry has a TTL with jitter.
“Common” is measured: kept by at least 6 in 10 of the 9 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Caching Strategies
A cache is a second copy of data that is allowed to be wrong for a bounded time. Before adding one, write down three things: how stale the data may be, how it gets invalidated, and what happens when the cache is down or empty. If you cannot answer all three, do not add the cache yet.
When to use
- Adding Redis, Memcached, an in-process cache, a CDN, or HTTP cache headers.
- Users see stale or wrong data, or one user sees another user's data.
- Hit ratio is low, evictions spike, or the database falls over when the cache restarts.
- Someone proposes "just cache it" for a slow endpoint or query.
Not for: finding the slow code path (performance-profiling) or fixing a slow query (sql-optimization). Fix those first when you can; a cache in front of a missing index hides the problem and adds a consistency problem.
Workflow
- Measure first. Confirm the slow path with numbers: p95 latency, request rate, source load (DB CPU, QPS). Check whether fixing the query or call is cheaper than caching it.
- Characterize the data. Read/write ratio, value size, number of distinct keys, who may see it (public, per tenant, per user), and the staleness budget per field ("prices may be 60 s old; account balances never").
- Pick the layer using the table below.
- Pick the pattern. Default to cache-aside with a TTL and delete-on-write.
- Design keys and TTLs (rules below).
- Design invalidation and write the consistency contract in a comment or doc next to the code.
- Protect hot keys against stampedes, and define behavior when the cache is down.
- Instrument hit ratio, latency, evictions and errors. Load test a cold cache.
- Roll out behind a flag. For critical data, compare cached and uncached results in shadow mode before trusting the cache.
Where to cache
| Layer | Good for | Watch out for |
| Browser HTTP cache | Static assets; per-user GET responses marked private | You cannot purge it. Control is only headers and URL versioning |
| CDN / shared proxy | Public pages, assets, public API responses | Must never store personalized responses. A wrong cache key or Vary leaks data |
| In-process memory | Small, hot, rarely changing data (config, reference tables) | Each instance has its own copy, so instances disagree. Bound the size. Lost on restart |
| Distributed cache (Redis, Memcached) | Data shared across instances, sessions, rate-limit counters | Extra network hop; becomes a critical dependency; evictions |
| Database side (materialized views, summary tables) | Expensive aggregates and reports | Refresh cost and staleness between refreshes |
A two-level cache (in-process with a short TTL in front of a distributed cache) works for very hot keys, but keep the local TTL short because there is no cross-instance invalidation unless you build a broadcast.
Patterns
| Pattern | How it works | Use when | Risk |
| Cache-aside (lazy loading) | Read: get from cache; on miss load from source and set with TTL. Write: update the source, then delete the key | The default for application caches | A race can re-insert a stale value; first read after expiry is slow |
| Read-through | A cache library calls a loader on miss | You want loading logic in one place | Same as cache-aside |
| Write-through | Writes update source and cache synchronously | Reads right after writes must hit a fresh cache | Slower writes; caches data nobody reads |
| Write-behind (write-back) | Writes go to the cache and are flushed to the source later | Very high write volume where some loss is acceptable (view counters) | Data loss on crash, ordering bugs. Never use for business records |
| Refresh-ahead | Popular keys are refreshed before they expire | Predictable hot keys with expensive loads | Wasted work on keys nobody reads anymore |
Keys
- Include every input the value depends on: entity ID, tenant ID, user ID when the value is per user, locale, currency, feature-flag variant, API or schema version. A missing input means serving the wrong data to someone.
- Namespace and version:
app:v3:product:{tenantId}:{productId}. To invalidate everything after a format change, bump the version prefix instead of flushing the whole cache. - Normalize inputs before building keys (sort query parameters, lowercase where case does not matter) so equal requests share one entry.
- Bound key length: hash long inputs with a stable hash (for example SHA-256 of the normalized query string).
- Serialize with a schema-tolerant format, or put the schema version in the key so a deploy with a new format does not read old entries.
TTLs
- Give every entry a TTL, even when you also invalidate explicitly. The TTL is the safety net for invalidations you missed.
- Derive the TTL from the staleness budget, not from the hit ratio you want.
- Add jitter (for example ±10-20%) so entries written together do not expire together.
- Use a shorter TTL for negative results ("not found").
Invalidation
| Strategy | How | Staleness |
| TTL only | Entries expire | Up to the TTL |
| Delete on write | After the source write commits, delete the key | Small race window (below) |
| Versioned keys | Put the entity's version or updated_at in the key | None for readers who know the version; old entries age out |
| Event-driven | Publish change events (outbox or CDC); consumers delete keys | Event delivery lag; works across services |
| Tag / surrogate-key purge | Tag CDN or cache entries and purge by tag | Purge propagation time |
Rules:
- Delete, do not update, on write. Updating the cache from the writer can race with another writer and leave the older value in place.
- Invalidate after the transaction commits, not before. Deleting first lets a concurrent reader reload the old row and cache it again.
- Know the cache-aside race: reader misses and reads the old row; writer commits and deletes the key; reader then writes the old value. The TTL bounds it. When that is not acceptable, use versioned keys or a compare-and-set on a version field.
- In-process caches are not invalidated across instances. Keep their TTL short or add a pub/sub broadcast.
Stampede protection
When a hot key expires, many requests miss at once and all hit the source.
- Request coalescing (single-flight): one load per key per process; other callers wait for that result. Go has
golang.org/x/sync/singleflight; elsewhere, keep a map of in-flight futures/promises per key. - Distributed lock for expensive loads:
SET lock:{key} {random-token} NX PX 10000in Redis. Only the holder loads; others serve the stale value or wait briefly. Release only if the token still matches, and let the expiry handle crashed holders. - Soft and hard TTL (stale-while-revalidate): after the soft TTL, one request refreshes in the background while others get the stale value; after the hard TTL, the entry is gone.
- Probabilistic early refresh: refresh with a probability that rises as expiry approaches, so refreshes spread out.
- Warm critical keys on deploy or failover when a cold cache would overload the source.
Negative caching
Cache "not found" results with a short TTL so repeated lookups for missing IDs (bots, typos, enumeration) do not reach the database. Delete the negative entry when the item is created.
Failure behavior
- A cache outage must not take the application down. Use short timeouts on cache calls (tens of milliseconds), then fall back to the source with load shedding or a circuit breaker (see resilience-patterns).
- Check whether the source survives a 100% miss rate. If it does not, record that as a capacity risk and plan warming or rate limiting.
- Set the eviction policy on purpose. Redis defaults to
maxmemory-policy noeviction(writes fail when memory is full); a pure cache usually wantsallkeys-lruorallkeys-lfu. Check withCONFIG GET maxmemory-policy. - Never store data only in the cache unless it is deliberately configured as a durable store.
- Flushing a production cache, changing eviction policy, or purging a CDN is a production change: stop and get confirmation first, and say what load the source will take.
HTTP caching quick defaults
| Response | Cache-Control |
Fingerprinted static assets (app.3f9a1c.js) | public, max-age=31536000, immutable |
| HTML documents | no-cache plus an ETag, so browsers revalidate each time |
| Public API GET that may be briefly stale | public, max-age=60, stale-while-revalidate=30 |
| Per-user responses | private, no-cache |
| Sensitive data (account pages, tokens, PII) | no-store |
no-cachemeans "store, but revalidate before use".no-storemeans "do not store".s-maxagesets the lifetime for shared caches (CDNs) separately from browsers.- Always send an explicit
Cache-Control. Without one, caches may apply heuristic freshness to responses that haveLast-Modified. Varymust list request headers that change the response (Accept-Encoding,Accept-Language).Vary: CookieorVary: Authorizationusually defeats a CDN; do not share-cache those responses at all.- Prefer versioned URLs over CDN purges.
Read references/http-caching.md when setting headers, configuring a CDN cache key, or debugging why a response was or was not cached.
Security
- Per-user or per-tenant data never goes into a shared cache under a key without the user or tenant ID.
- Make sure the CDN never caches responses that carry
Set-Cookieor depend onAuthorization, and that it honorsprivateandno-store. - Cache poisoning: request headers that change the response (for example
X-Forwarded-Host) but are not part of the cache key let an attacker store a malicious response for everyone. Either add such headers to the key or stop them from affecting the response. - Cache servers should not be reachable from the internet and should require authentication and TLS where the network is not trusted.
Metrics
- Hit ratio per key prefix, hit vs miss latency, evictions, memory used, key count, cache errors and timeouts, and source load.
- Alert on a sudden hit-ratio drop and on eviction spikes.
- For HTTP, inspect
Ageand the CDN's cache status header when debugging.
Anti-patterns
- Caching to hide a missing index or N+1 query. Fix the query first.
- No TTL. One missed invalidation keeps wrong data forever.
- Updating the cache on write instead of deleting the key.
- Invalidating before the commit.
- Shared cache key without the user or tenant. This is a data leak, not a performance bug.
- Treating the cache as the source of truth.
- Caching errors (5xx, timeouts) for long periods. Cache failures, if at all, only for seconds.
- Unbounded in-process maps used as caches. Use a size-bounded LRU.
- Caching everything "just in case". A low hit ratio adds latency, memory cost and bugs.
Done when
- The staleness budget, invalidation method and cache-down behavior are written next to the code.
- Keys include every input; per-user and per-tenant data is never shared unkeyed.
- Every entry has a TTL with jitter; hot keys have stampede protection.
- Tests cover hit, miss, invalidation after a write, and cache unavailable.
- Hit ratio, evictions and latency are on a dashboard, and the source is known to survive a cold cache.
References
- references/http-caching.md — read when choosing
Cache-Controldirectives, setting up a CDN, or checking headers with curl.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

