cache-strategy
Skill12 starsChanged 4 months agoMITView on GitHub
Implement a permanent cache-first strategy: check cache before hitting the DB, write to cache on first read, invalidate only when data changes. No TTL timeouts. Modeled on Play Framework's cache model. Use when asked to "add caching", "implement cache strategy", "cache DB results", or "reduce DB load".
- Reads credentials
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 174
Use a consistent key naming scheme across the codebase:
- Distinctive: line 183
Document the convention in a comment at the top of your cache module:
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/cache-strategy
Implement a permanent cache-first strategy: read from cache, fall back to DB on miss, write to cache, invalidate only on data mutation. No expiry timers — the cache stays valid until the underlying data changes.
Philosophy
The goal is one DB read per data item, ever (until it changes):
READ: cache hit? → return cached value
cache miss? → query DB → write to cache → return value
WRITE: update DB → invalidate (or update) cache entry → doneThis is the Play Framework model: no TTL, no polling, no background refresh. The cache is always warm and always correct because it is invalidated precisely when the source changes.
Why permanent over TTL?
- TTL caches trade stale reads for simplicity — every expiry is a guaranteed DB hit even if nothing changed
- Permanent caches with invalidation give you zero unnecessary DB hits and zero stale reads
- TTL is only appropriate when you can't know when data changes (third-party APIs, etc.)
Step 1: Detect cache infrastructure
# Check for Redis
grep -rn "redis\|Redis\|REDIS_URL" --include="*.json" --include="*.env*" --include="*.yml" --include="*.yaml" --include="*.rb" --include="*.py" --include="*.js" --include="*.ts" . 2>/dev/null | grep -v node_modules | head -10
# Check for Memcached
grep -rn "memcached\|Memcached\|MEMCACHE" --include="*.json" --include="*.env*" --include="*.yml" . 2>/dev/null | grep -v node_modules | head -5
# Check for in-process cache (node-cache, lru-cache, etc.)
grep -rn "lru-cache\|node-cache\|memory-cache\|caffeine\|guava.*cache" --include="*.json" --include="*.js" --include="*.ts" --include="*.java" . 2>/dev/null | grep -v node_modules | head -5Report what's available. If no cache layer exists, recommend Redis as the default and offer to add the client library.
Step 2: Identify cacheable DB reads
Scan for DB query patterns and classify by cacheability:
High-value cache candidates (read often, change rarely):
- User profiles, settings, preferences
- Product/item details
- Configuration tables
- Reference data (categories, tags, enums stored in DB)
- Aggregates that are expensive to compute (counts, sums)
Poor cache candidates (skip these):
- Real-time data (live inventory counts, chat messages)
- Per-session data
- Data that changes on every write (e.g., counters that increment on every request)
# Find the most-called query patterns
grep -rn "\.find\b\|\.findById\|\.findOne\|\.where\|SELECT" \
--include="*.rb" --include="*.py" --include="*.js" --include="*.ts" \
. 2>/dev/null | grep -v node_modules | grep -v test | grep -v spec | head -30For each candidate, note:
- Cache key pattern (e.g.,
user:{id},product:{slug}) - Where it's written/updated (to know where to place invalidation)
Step 3: Implement cache-first reads
For each cacheable read, apply this pattern:
Redis (Node.js / TypeScript)
async function getUser(id: string) {
const cacheKey = `user:${id}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const user = await db.users.findById(id);
await redis.set(cacheKey, JSON.stringify(user)); // no TTL — permanent until invalidated
return user;
}Redis (Ruby / Rails)
def find_user(id)
Rails.cache.fetch("user:#{id}") do
User.find(id) # only called on cache miss
end
endRedis (Python)
def get_user(user_id: int):
cache_key = f"user:{user_id}"
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached)
user = db.session.get(User, user_id)
redis_client.set(cache_key, json.dumps(user.to_dict())) # no expiry
return userPlay Framework (Scala)
def getUser(id: Long): Future[User] = {
cache.getOrElseUpdate(s"user:$id") {
userRepository.findById(id)
}
}Step 4: Implement cache invalidation on writes
For every place that mutates cached data, add invalidation immediately after the DB write succeeds:
Node.js / TypeScript
async function updateUser(id: string, data: Partial<User>) {
const user = await db.users.update(id, data);
await redis.del(`user:${id}`); // invalidate on change
return user;
}Ruby / Rails
def update_user(id, attrs)
user = User.find(id).tap { |u| u.update!(attrs) }
Rails.cache.delete("user:#{id}")
user
endImportant: invalidate on delete too:
async function deleteUser(id: string) {
await db.users.delete(id);
await redis.del(`user:${id}`);
}For lists/collections, invalidate the collection key when any item in it changes:
await redis.del(`user:${id}`); // the specific item
await redis.del(`users:list`); // any cached list that included this itemStep 5: Cache key conventions
Use a consistent key naming scheme across the codebase:
<entity>:<identifier> → user:123, product:slug-name
<entity>:list:<filter> → products:list:category-5
<entity>:<id>:<relation> → user:123:orders
aggregate:<entity>:<filter> → aggregate:orders:user-123-totalDocument the convention in a comment at the top of your cache module:
// Cache key conventions:
// user:{id} — single user record
// users:list — paginated user list (invalidate on any user change)
// user:{id}:orders — orders belonging to a userStep 6: Cache warming (optional)
For critical data that must never have a cold-start miss, add a warm-up step on app boot:
async function warmCache() {
const activeUsers = await db.users.findActiveUsers();
await Promise.all(
activeUsers.map(u => redis.set(`user:${u.id}`, JSON.stringify(u)))
);
}Step 7: Summary
Report what was implemented:
## Cache Strategy Applied
### Cache layer: Redis (permanent, no TTL)
### Reads cached (X total)
- user:{id} — UserService.getUser()
- product:{slug} — ProductService.findBySlug()
- ...
### Invalidation points added (Y total)
- user:{id} — UserService.updateUser(), UserService.deleteUser()
- product:{slug} — ProductService.updateProduct()
- ...
### Estimated DB load reduction
Before: ~X DB queries/min
After: ~Y DB queries/min (first-read only, then cache)Note: for data that comes from third-party APIs or sources where you can't intercept writes, use a long TTL (hours, not minutes) as a fallback — but flag these separately so they can be revisited if an invalidation webhook becomes available.
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- manastalukdar/ai-devstudio★ 1same name
- Abdox-menara/guard-skills★ 0same name
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.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- a11ySkill
- articleSkill
- basic-reviewSkill
- clean-codeSkill
- cli-for-agentSkill
- content-writerSkill
- daily-qaSkill
- dbmap-auto-offSkill
- dbmapSkill
- db-optimizeSkill
- debugSkill
- defenseSkill
- diff-walkthroughSkill
- evalSkill
- executing-plansSkill
- finish-branchSkill
- fuzzSkill
- handoffSkill
- humanizeSkill
- iac-scanSkill
- kb-advisorSkill
- minimalist-reviewSkill
- pentestSkill
- perf-profileSkill
- playwrightSkill
- pricingSkill
- qa-fullSkill
- quota-resilienceSkill
- subagent-driven-developmentSkill
- superskills-doctorSkill
- superskills-upgradeSkill
- tapestrySkill
- tddSkill
- test-coverageSkill
- validate-ideaSkill
- verifySkill
- web-perfSkill
- worktreesSkill
- write-planSkill
- youtubeSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
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.

