caching-strategies
Skill1 starsChanged 7 months agoMITView on GitHub
Caching design across HTTP, CDN, application, and client layers. Use when: adding cache headers, configuring CloudFront or CDN behavior, implementing Redis or in-memory caching, designing IndexedDB or service worker caches, choosing cache invalidation strategies, auditing an existing application for missing or misconfigured caches, or optimizing API response times. Covers HTTP cache-control, CDN edge caching, application-level caching, client-side storage, cache invalidation patterns, and cache warming.
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
- reddygopalkrishna/developer-agent-skillstheir 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
Used with
Projects with it also use observability.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
OpenAISkillobservability
Vercel Observability expert guidance — Drains (logs, traces, speed insights, web analytics), Web Analytics, Speed Insights, runtime logs, custom events, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.
Also in 3 projects that use this
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
- reddygopalkrishna/developer-agent-skills★ 8same 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
- 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 repository42 other files it gives its agents.
- accessibility-testingSkill
- alternatives-analysisSkill
- anomaly-detectionSkill
- api-designSkill
- async-messagingSkill
- chartsSkill
- cicdSkill
- code-reviewSkill
- commsSkill
- critical-feedbackSkill
- data-analysisSkill
- databaseSkill
- data-migrationsSkill
- data-visualizationSkill
- dependency-managementSkill
- dockerSkill
- error-handlingSkill
- feature-flagsSkill
- git-workflowSkill
- goalsSkill
- ide-setupSkill
- large-changeSkill
- logging-metricsSkill
- markdown-docsSkill
- mcp-serverSkill
- meeting-agendasSkill
- okrsSkill
- presentationsSkill
- product-managementSkill
- prompt-writingSkill
- pythonSkill
- reclassificationSkill
- release-managementSkill
- retrospectivesSkill
- roadmap-planningSkill
- securitySkill
- stakeholder-commsSkill
- statisticsSkill
- status-updatesSkill
- testingSkill
- ticket-writingSkill
- typescriptSkill
The file
We split this skill into the rules it gives an agent
6 rules. 0 are common, 4 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 9 edited versions we read keep these.
4
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
- Very specific: line 62
Use content-hashed filenames (app.a1b2c3.js) so the URL changes when content changes.
- Very specific: line 87
Use for sensitive data (auth tokens, PII, financial data).
- Distinctive: line 211
Write to cache AND database on every update.
- Distinctive: line 214
Use when read-after-write consistency is required.
- Distinctive: line 330
When to warm: After deploys, after cache flushes, for predictably popular content.
- Distinctive: line 352
When auditing an existing application for caching:
“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
When to Use
- Adding HTTP cache headers to API responses or static assets
- Configuring CDN (CloudFront, Cloudflare) cache behavior and TTLs
- Implementing Redis, Memcached, or in-memory caching for API responses or computed data
- Designing client-side caching with IndexedDB, localStorage, or service workers
- Choosing a cache invalidation strategy for a specific use case
- Auditing an existing application for missing, stale, or misconfigured caches
- Reducing database load or API latency with read-through caching
- Warming caches on deploy or after invalidation
Core Principle: Cache Close to the Consumer
The best cache is the one closest to the requester. Cache at the edge before the origin, at the application before the database, at the client before the network. Each cache layer reduces load on the layers behind it.
Cache Layers (Outer to Inner)
| Layer | Where | TTL range | Invalidation | Example |
| Browser | Client | Seconds to years | Cache-Control headers, versioned URLs | Static assets, API responses |
| Service Worker | Client | App-controlled | Programmatic via Cache API | Offline-first PWA data |
| CDN / Edge | Edge PoP | Minutes to days | Purge API, TTL expiry | CloudFront, Cloudflare |
| API Gateway | Origin edge | Seconds to minutes | TTL expiry | API Gateway response cache |
| Application | Server memory or Redis | Seconds to hours | Explicit delete, TTL | Computed aggregations, session data |
| Database | Query layer | Automatic | Query plan cache, materialized views | PostgreSQL query cache |
Rule: Never Cache Authentication or User-Specific Data at Shared Layers
Shared caches (CDN, API Gateway) must never serve one user's data to another. Mark user-specific responses as private or no-store.
HTTP Cache-Control Headers
Static Assets (CSS, JS, images, fonts)
Cache-Control: public, max-age=31536000, immutableUse content-hashed filenames (app.a1b2c3.js) so the URL changes when content changes. Set max-age to 1 year. The immutable directive tells browsers not to revalidate.
API Responses (Public, Cacheable)
Cache-Control: public, max-age=60, stale-while-revalidate=300max-age=60: Fresh for 60 seconds.stale-while-revalidate=300: Serve stale for up to 5 minutes while fetching fresh copy in background.
API Responses (Private, User-Specific)
Cache-Control: private, max-age=0, must-revalidatePrevents CDN/proxy caching. Browser may cache but must revalidate on every request.
No Caching
Cache-Control: no-storeUse for sensitive data (auth tokens, PII, financial data). no-store is stronger than no-cache — it prevents storage entirely.
ETag and Conditional Requests
# FastAPI example
from hashlib import sha256
@app.get("/api/config")
def get_config(request: Request) -> Response:
data = load_config()
body = json.dumps(data)
etag = sha256(body.encode()).hexdigest()[:16]
if request.headers.get("if-none-match") == etag:
return Response(status_code=304)
return Response(
content=body,
headers={"ETag": etag, "Cache-Control": "private, max-age=0, must-revalidate"},
)CDN / Edge Caching
CloudFront Cache Behavior
| Path pattern | TTL | Origin | Cache policy |
/static/* | 1 year | S3 | CachingOptimized (query strings ignored) |
/api/public/* | 60s | ALB/Lambda | Forward Accept, Accept-Encoding |
/api/private/* | 0 | ALB/Lambda | CachingDisabled |
Default (*) | 1 day | S3 | CachingOptimized |
Cache Key Design
Include only what differentiates responses:
- Good: Path +
Accept-Languageheader (for localized content). - Bad: Path +
Authorizationheader (every user gets a different cache entry — defeats purpose). - Rule: Fewer cache key components = higher hit rate.
Invalidation
# Invalidate specific paths
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/api/public/products" "/api/public/categories"
# Invalidate everything (expensive — use sparingly)
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/*"Prefer versioned URLs over invalidation. Invalidation is slow (minutes), costly at scale, and cannot be undone.
Application-Level Caching
Redis / ElastiCache
import json
import hashlib
from collections.abc import Callable
from typing import Any
import redis
cache = redis.Redis(host="cache.example.com", port=6379, decode_responses=True)
def cached(prefix: str, ttl_seconds: int = 300) -> Callable[..., Any]:
"""Decorator for caching function results in Redis."""
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
def wrapper(*args: Any, **kwargs: Any) -> Any:
key_data = json.dumps({"args": args, "kwargs": kwargs}, sort_keys=True)
cache_key = f"{prefix}:{hashlib.sha256(key_data.encode()).hexdigest()[:16]}"
cached_value = cache.get(cache_key)
if cached_value is not None:
return json.loads(cached_value)
result = func(*args, **kwargs)
cache.setex(cache_key, ttl_seconds, json.dumps(result))
return result
return wrapper
return decorator
@cached("products", ttl_seconds=60)
def get_products(category: str) -> list[dict[str, Any]]:
return db.query_products(category)In-Memory Caching (Python)
from functools import lru_cache
@lru_cache(maxsize=256)
def compute_expensive_result(input_hash: str) -> dict[str, Any]:
"""Cache in process memory. Use for immutable inputs only."""
return heavy_computation(input_hash)Warning: In-memory caches are per-process. In Lambda, each concurrent execution has its own cache. Use Redis for shared state.
Cache-Aside (Read-Through) Pattern
- Check cache.
- On miss: query database, write to cache, return result.
- On hit: return cached value.
This is the most common pattern. The application manages the cache explicitly.
Write-Through Pattern
- Write to cache AND database on every update.
- Reads always hit cache.
Use when read-after-write consistency is required. More complex, but eliminates stale reads.
Client-Side Caching
React Query / TanStack Query
const { data } = useQuery({
queryKey: ["products", categoryId],
queryFn: () => fetchProducts(categoryId),
staleTime: 5 * 60 * 1000, // Fresh for 5 minutes
gcTime: 30 * 60 * 1000, // Keep in memory for 30 minutes
refetchOnWindowFocus: false, // Don't refetch on tab switch
});IndexedDB for Offline Data
async function getCachedOrFetch<T>(
storeName: string,
key: string,
fetcher: () => Promise<T>,
maxAgeMs: number = 5 * 60 * 1000,
): Promise<T> {
const cached = await idb.get(storeName, key);
if (cached && Date.now() - cached.timestamp < maxAgeMs) {
return cached.data as T;
}
const fresh = await fetcher();
await idb.put(storeName, { key, data: fresh, timestamp: Date.now() });
return fresh;
}Service Worker Cache
// Cache-first for static assets, network-first for API
self.addEventListener("fetch", (event: FetchEvent) => {
const url = new URL(event.request.url);
if (url.pathname.startsWith("/static/")) {
event.respondWith(caches.match(event.request).then((r) => r ?? fetch(event.request)));
} else if (url.pathname.startsWith("/api/")) {
event.respondWith(
fetch(event.request)
.then((response) => {
const clone = response.clone();
caches.open("api-cache").then((cache) => cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request).then((r) => r ?? new Response("Offline", { status: 503 }))),
);
}
});Cache Invalidation Strategies
| Strategy | How it works | Best for |
| TTL expiry | Cache entry expires after fixed duration | Tolerant of slight staleness (product listings, feeds) |
| Event-driven purge | Publish event on write → subscriber deletes cache key | Strong consistency needs (user profile, permissions) |
| Versioned keys | Include version/hash in cache key; new version = new key | Configuration, feature flags |
| Write-through | Update cache on every write | Read-heavy with frequent writes |
| Cache stampede prevention | Lock during recomputation; others wait or serve stale | Expensive computations with high concurrency |
Cache Stampede Prevention
import time
LOCK_TTL = 10 # seconds
def get_with_lock(key: str, compute_fn: Callable[[], Any], ttl: int = 300) -> Any:
"""Prevent thundering herd on cache miss."""
value = cache.get(key)
if value is not None:
return json.loads(value)
lock_key = f"lock:{key}"
if cache.set(lock_key, "1", nx=True, ex=LOCK_TTL):
try:
result = compute_fn()
cache.setex(key, ttl, json.dumps(result))
return result
finally:
cache.delete(lock_key)
else:
# Another process is computing — wait briefly and retry
time.sleep(0.1)
value = cache.get(key)
return json.loads(value) if value else compute_fn()Cache Warming
Warm caches on deploy to avoid cold-start latency:
def warm_cache() -> None:
"""Call after deployment to pre-populate critical caches."""
popular_categories = ["electronics", "clothing", "home"]
for category in popular_categories:
get_products(category) # Triggers cache-aside populationWhen to warm: After deploys, after cache flushes, for predictably popular content. When not to warm: For long-tail content (millions of unique keys) — let demand drive caching.
Anti-Patterns
| Anti-Pattern | Problem | Fix |
| Cache everything forever | Stale data served indefinitely | Set explicit TTLs on every cache entry |
| No cache at all | Every request hits origin | Add Cache-Control headers at minimum |
| Caching user-specific data in shared CDN | Data leaks between users | Use Cache-Control: private or no-store |
| Cache key includes irrelevant parameters | Low hit rate, wasted memory | Minimize cache key components |
| No invalidation strategy | Stale data until TTL expires | Use event-driven purge for mutable data |
| Cache stampede on popular keys | Origin overwhelmed on expiry | Use locking or stale-while-revalidate |
| Caching errors | Error responses cached and served to all | Never cache 5xx; cache 404 briefly if intentional |
| Using cache as primary data store | Data loss on eviction or restart | Cache is ephemeral — always have a source of truth |
Audit Checklist
When auditing an existing application for caching:
- Static assets have long
max-agewith content-hashed filenames - API responses have appropriate
Cache-Controlheaders (not missing entirely) - User-specific responses use
privateorno-store - Sensitive data (auth, PII) uses
no-store - CDN cache key includes only differentiating parameters
- Application cache has TTLs on every key (no indefinite caching)
- Cache invalidation strategy exists for mutable data
- Cache stampede prevention exists for expensive computations
- Error responses are not cached (or cached only briefly and intentionally)
- Cache warming runs on deploy for critical paths
- Client-side
staleTime/gcTimeconfigured for data fetching library - Service worker cache strategy matches data freshness requirements
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.

