backend-reliability-scale
Skill1 starsChanged 15 days agoMITView on GitHub
Catch the failures that only appear under real traffic - unbounded queries, N+1 loops, missing indexes, serverless connection exhaustion, missing timeouts, and PII written to logs. Use before launch, before a traffic spike, or when an app is slow or falling over in production.
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 3 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 12
Before launch, a marketing push, or any expected traffic spike
- Very specific: line 48
Every column used in a WHERE, ORDER BY or JOIN needs an index, and a foreign key does not always get one automatically.
- Very specific: line 50
Check the obvious ones first: user_id, tenant_id, created_at, status, and anything used for lookups such as email or slug.
- Very specific: line 58
Use a singleton client cached on globalThis, not a new client per request.
- Distinctive: line 59
Put a pooler in front (PgBouncer, Supabase pooler, Prisma Accelerate, Neon pooling) and use the pooled connection string in serverless.
- Very specific: line 87
Run python3 .aegis/scripts/aegis_scan.py .
- Distinctive: line 90
Check client instantiation: one per process, or one per request?
≈ “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
Backend Reliability, Scale & Privacy
These bugs pass every test and every demo, because they only appear at a row count or a request rate you did not have yet. The app does not break when you ship it. It breaks the day it succeeds.
When to Trigger
- Before launch, a marketing push, or any expected traffic spike
- The app is timing out, running out of memory, or the database is pegged
- The user asks "will this handle load?", "why is it slow?", "is it production-ready?"
1. Unbounded Queries (REL-01)
findMany() with no limit is fine with 100 rows and fatal at 100,000. It loads every row into memory, serialises all of it, and takes the process down with it.
const orders = await prisma.order.findMany(); // BROKEN
const orders = await prisma.order.findMany({ take: 50 }); // CORRECTAudit every list endpoint, every export, every admin dashboard and every SELECT *. Enforce a maximum page size on the server — never let the client pass limit=100000. Prefer cursor pagination over OFFSET, which gets slower the deeper the user goes.
Watch for the subtler version: a bounded query whose result is then fully loaded into memory for an in-process .filter() or .reduce(). Push that work into the database.
2. N+1 Query Loops (REL-02)
One query per iteration. Ten items in development, ten thousand in production.
for (const item of items) { // BROKEN - N round trips
const user = await db.user.findUnique({ where: { id: item.userId } });
}
const users = await db.user.findMany({ // CORRECT - one round trip
where: { id: { in: items.map(i => i.userId) } },
});
const byId = new Map(users.map(u => [u.id, u]));The same pattern hides inside .map(async ...), inside GraphQL resolvers (use a dataloader), and in ORM lazy-loading when a relation is accessed in a loop without include/join.
3. Indexes
Every column used in a WHERE, ORDER BY or JOIN needs an index, and a foreign key does not always get one automatically. The symptom is a query that is instant on a seeded dev database and times out on real data.
Check the obvious ones first: user_id, tenant_id, created_at, status, and anything used for lookups such as email or slug. For a query filtering on two columns, a single composite index usually beats two separate ones — and column order matters.
Confirm with EXPLAIN ANALYZE rather than guessing. A sequential scan on a large table is the finding.
4. Serverless Connection Exhaustion
Each serverless invocation can open its own database connection. A hundred concurrent requests exhaust the connection limit and every subsequent request fails — including the ones that would have succeeded.
- Use a singleton client cached on
globalThis, not a new client per request. - Put a pooler in front (PgBouncer, Supabase pooler, Prisma Accelerate, Neon pooling) and use the pooled connection string in serverless.
- Set a connection limit per instance deliberately rather than taking the default.
5. Missing Timeouts (REL-04)
An upstream call with no timeout holds your function open until the platform kills it, consuming concurrency the whole time. One slow dependency becomes your outage.
await fetch(url, { signal: AbortSignal.timeout(10_000) });Set timeouts on outbound HTTP, database queries, and any lock acquisition. Add a retry with exponential backoff and a bounded attempt count — naive immediate retries amplify an upstream incident into a self-inflicted denial of service.
6. Sensitive Data In Logs (REL-03)
Logs go to third-party services, persist for months, and are readable by anyone with dashboard access. A password in a log is a breach with a long tail.
Flag console.log and logger calls carrying passwords, tokens, API keys, card numbers, or personal data. Watch for the indirect cases: logging an entire request body, an entire user object, or an error object whose message embeds the connection string. Log an identifier, never the value, and configure redaction at the logger.
7. Failure Behaviour
- What happens when the database is down — a clear error, or an unhandled rejection that takes the process with it?
- Are background jobs idempotent? They will be retried.
- Does a partial failure leave data half-written? Wrap multi-step writes in a transaction.
- Is there a health check that actually checks dependencies, rather than returning 200 unconditionally?
Investigation Protocol
- Run
python3 .aegis/scripts/aegis_scan.py . --output .aegis/scan-report.jsonand read theRELIABILITYdomain. - List every database query. Mark each bounded or unbounded, indexed or not.
- Find every loop containing an
awaiton a database or network call. - Check client instantiation: one per process, or one per request?
- Grep outbound calls for a timeout. Absence is the finding.
- Estimate the row count each query will face in a year, not today.
Reporting
Say when it breaks, not just that it is suboptimal: "this endpoint loads the full orders table; at ~50k rows it will exhaust the 1 GB function memory limit". REL findings are reported at MEDIUM confidence because a pattern cannot know your data volume — confirm against real table sizes before promoting one to CONFIRMED.
In this repository12 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- llms-full.txtllms.txt
- llms.txtllms.txt
- ai-llm-security-shieldSkill
- architecture-threat-modelSkill
- artifact-leak-hunterSkill
- payments-logic-auditorSkill
- production-preflight-gateSkill
- security-redteam-loopSkill
- ui-ux-bug-finderSkill
- vibe-aegisSkill
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.

