Backend dev
49 production-grade AI agent skills (SKILL.md) for Claude Code, Codex & Antigravity — system design, DevOps, security, QA, and more. MIT licensed, open source.
npx -y skills add CODE-SAURABH/OpenSkills --skill backend-devAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 11 days oldThe repository was created 11 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Production-grade backend development guidance — language and framework agnostic. Use when the user asks to build an API, design a backend service, create a database schema, implement authentication, write business logic, structure a backend project, or produce server-side code of any kind.
SKILL.md
17.5 KB, as published. Nobody here has run it
Backend Development
Approach every backend task as a senior engineer who has been burned by shortcuts in production. You are not here to make something that works once — you are here to make something that works at 3am on a Saturday when no one is watching. Every decision defaults to correctness, observability, and maintainability over cleverness or speed.
Step 0: Ground the Work Before Writing a Line
Before any code, establish:
- What is this service's single responsibility? Name it in one sentence. If you can't, the scope needs splitting.
- What are the data boundaries? What goes in, what comes out, what persists.
- What can fail? External calls, DB writes, auth checks — enumerate failure points before coding happy paths.
- What does done look like? Define the contract: endpoint, schema, or interface the caller expects.
State these four things explicitly to the user if the brief is ambiguous. Never silently assume.
Execution Workflow
Before writing any implementation code, work through these steps in order:
- Understand the contract — name the endpoint or function signature, its inputs, outputs, and error cases
- Map the layers — decide what belongs in
api/,services/,repositories/, and whether any new models or errors are needed - Identify external dependencies — list every DB call, external API, queue, or file system access the feature requires
- Enumerate failure modes — for each dependency, ask: what happens if it is slow, returns nothing, or fails entirely?
- Define the tests first — name the unit test cases before writing the implementation; if you cannot name them, the design is unclear
- Then implement — follow the layer rules, apply the coding standards, handle every failure mode you named
Do not skip to implementation. A feature built on a vague contract costs more to fix than the time spent grounding it.
Folder Structure
Always generate a clean, domain-driven folder structure. The structure must communicate the architecture at a glance. Follow this pattern regardless of language or framework:
project-root/
├── src/
│ ├── api/ # Route handlers / controllers — thin layer only
│ │ └── v1/ # Versioned from day one
│ ├── services/ # Business logic — no framework code here
│ ├── repositories/ # All data access — DB queries live here only
│ ├── models/ # Domain entities and data types
│ ├── middleware/ # Auth, logging, rate limiting, error handling
│ ├── config/ # Environment config, constants, feature flags
│ ├── utils/ # Pure helper functions — stateless, no side effects
│ └── errors/ # Custom error classes and error codes
├── tests/
│ ├── unit/ # One test file per source file
│ ├── integration/ # Tests that hit real DB / external services
│ └── fixtures/ # Shared test data and factories
├── migrations/ # Database migration files — versioned, never edited
├── scripts/ # One-off ops scripts, seeding, utilities
├── docs/ # API docs, ADRs, architecture diagrams
├── .env.example # Template — never commit .env
├── Dockerfile # Multi-stage, non-root user
├── docker-compose.yml # Local dev environment
└── README.md # Setup, run, test — nothing else
Rules:
api/handlers must be thin — validate input, call a service, return a response. No business logic.services/must be framework-agnostic — they should be callable from a test without spinning up a server.repositories/are the only place that know about the database. No raw queries elsewhere.- Never cross layers — a
repositorynever calls aservice, amodelnever importsmiddleware.
API Design
Every API must be designed contract-first. Define the interface before implementing it.
Mandatory conventions:
- Version from day one:
/api/v1/resource - Use correct HTTP methods: GET (read), POST (create), PUT (replace), PATCH (partial update), DELETE (remove)
- Return consistent HTTP status codes — never return 200 for an error
- For new REST APIs, default to a consistent response envelope:
Use this as the starting point unless the project has an established convention — consistency within a codebase matters more than the specific shape.// Success { "data": { ... }, "meta": { ... } } // Error { "error": { "code": "RESOURCE_NOT_FOUND", "message": "...", "details": [...] } } - Paginate all list endpoints — cursor-based preferred for large datasets, offset for simple cases
- Never expose stack traces or database errors in responses
- Every endpoint must have an OpenAPI/schema definition — auto-generate where possible
Request handling checklist for every endpoint:
- Validate and sanitize all input at the boundary before it touches any logic
- Authenticate — verify identity
- Authorize — verify permission for this specific resource
- Execute business logic via service layer
- Return typed, documented response
Code Quality Standards
Every piece of generated backend code MUST follow these rules — no exceptions:
Types & Contracts
- All function signatures must have explicit input and output types
- Define data contracts (schemas/interfaces/structs) for every external boundary: API input, API output, DB row, event payload
- No implicit any / untyped parameters — if a type is unknown, make it explicit and handle it
Error Handling
- Never swallow errors silently — every caught error must be logged or re-thrown
- Use custom error classes with error codes, not raw strings
- Distinguish operational errors (invalid input, not found) from programmer errors (null dereference, wrong type)
- All async operations must have error handling — no unhandled promise rejections, no unchecked results
- Errors bubble up to a single top-level error handler that formats and logs them consistently
Defensive Programming
- Validate all external input: API requests, environment variables, config values, message queue payloads
- Fail fast — check preconditions at the top of a function, not buried in the middle
- Never trust external data: third-party APIs, user uploads, webhook payloads
Naming & Clarity
- Functions named as verbs:
getUserById,sendWelcomeEmail,calculateTax - Variables named as nouns:
userId,orderTotal,connectionPool - No abbreviations unless universally understood (
id,url,http) - One concept per function — if a function does two things, split it
- Comments explain why, not what — if the code needs a comment to explain what it does, rewrite the code
Constants & Configuration
- No magic numbers or magic strings anywhere in business logic
- All configuration comes from environment variables — never hardcoded
- Group related constants into named objects/enums
- Every required environment variable must be validated on startup — crash early with a clear message rather than failing mysteriously later
Database Rules
- Migrations only — never mutate a schema manually; every change is a versioned, reversible migration file
- Indexes driven by query patterns — add indexes for the queries you actually run; over-indexing slows writes; document the query that justifies each index in the migration comment
- Transactions when atomicity is required — wrap multi-step writes that must succeed or fail together in a transaction; not every multi-row operation needs one — use judgement
- Soft deletes when the business requires it — use
deleted_attimestamps when audit history, undo, or compliance matters; hard deletes are correct when data has no retention requirement - Timestamps everywhere — every table gets
created_atandupdated_atby default - Parameterized queries only — never interpolate user input into a query string
- Connection pooling — configure min/max pool size; never open unbounded connections
- Read replicas when scale requires it — design repositories so read/write routing can be introduced later without touching service code; do not add the complexity before you need it
SOLID and Dependency Design
Apply SOLID principles as engineering judgement, not as a checklist. The goal is code that is easy to change, easy to test, and honest about its dependencies.
- Single Responsibility — a module has one reason to change. A service that sends emails and processes payments will be painful to test and painful to change. Split them.
- Open/Closed — extend behaviour by adding code, not by editing existing logic. New payment providers, new notification channels, new export formats should slot in without touching working code.
- Liskov Substitution — any implementation of an interface must honour the contract of that interface. A mock in tests must behave like the real thing in the ways the caller depends on.
- Interface Segregation — callers should not depend on methods they do not use. A
UserRepositoryinterface used by the auth service should not expose bulk-export methods only the admin service needs. - Dependency Inversion — high-level modules (services) depend on abstractions, not on concrete implementations (a specific DB client, a specific email provider). Wire concrete implementations at the composition root.
- Composition Root — all dependency wiring happens in one place at startup. Services do not construct their own dependencies. This makes the dependency graph readable and testable.
Avoid over-engineering. Do not introduce abstractions speculatively. Create an abstraction only when one of these is true:
- Multiple implementations exist now or are planned imminently
- The dependency crosses an external boundary (DB, HTTP, file system, queue)
- The dependency is nondeterministic (time, random, environment)
- It must be replaced in tests with a controlled substitute
- It represents a stable domain capability that multiple consumers will share
If none of these apply, use the concrete type directly.
Reliability and External Dependencies
Every call that leaves the process boundary is a call that can fail, hang, or return inconsistent results. Design for this explicitly.
- Timeouts — set an explicit timeout on every outbound call; never rely on the remote end to close the connection
- Retries with backoff — retry transient failures (network errors, 429s, 503s) with exponential backoff and jitter; do not retry non-idempotent operations unless the operation is safe to repeat
- Idempotency — design write operations to be safely retryable; use idempotency keys for payment and order operations; ensure that processing a message twice produces the same outcome as processing it once
- Message queues — use queues to decouple producers from consumers and to absorb load spikes; consumers must be idempotent; dead-letter queues are mandatory for any queue used in production
- Transactional outbox — when a DB write and a message publish must happen together, write the event to an outbox table in the same transaction as the business data, then publish from the outbox asynchronously; this prevents silent message loss on process crash
Authentication & Authorization
- Use short-lived access tokens + refresh token rotation as the default auth pattern
- Hash all passwords with a strong adaptive algorithm (bcrypt cost ≥ 12, or argon2id)
- Store tokens in httpOnly cookies for browser clients; never in localStorage
- Implement RBAC — every protected route checks both authentication (who are you?) and authorization (can you do this to this resource?)
- Verify resource ownership on every request — a user fetching
/orders/123must own order 123 - Implement account lockout after repeated failed attempts
- Rotate secrets on a schedule; invalidate all sessions on password change
- Never log passwords, tokens, secrets, or PII in any log line — ever
Unit Testing
Every service function, utility, and repository method gets a unit test. No exceptions.
Structure of every test file:
describe('<ModuleName>', () => {
describe('<methodName>', () => {
it('returns X when given valid input Y')
it('throws ErrorCode.X when input is missing')
it('calls dependency Z exactly once with correct args')
it('does not call dependency Z when condition is false')
})
})
Rules:
- Test behaviour, not implementation — test what a function does, not how it does it
- One assertion concept per test — a test that checks three things is three tests
- Mock all external dependencies (DB, HTTP, file system, clock) in unit tests — a unit test never hits the network or disk
- Use factories/builders for test data — never hardcode raw objects scattered across test files
- Test the sad path as rigorously as the happy path: invalid input, missing data, dependency failures, edge cases
- Every test must be independent — no shared mutable state between tests, no order dependency
- Aim for 80%+ line coverage on
services/andutils/; 100% on critical auth and payment paths
For every public function, generate tests that cover:
- Happy path with valid input
- Missing required input
- Invalid type / format input
- Boundary values (empty string, zero, max int, null)
- Dependency throws an error
- Dependency returns empty / null
- Concurrent / race condition if applicable
Integration Testing
- Integration tests live in
tests/integration/and run against a real database (use containers or an in-memory equivalent) - Test the full stack from HTTP request to DB response for critical paths
- Each integration test must clean up its own data — use transactions that rollback, or explicit teardown
- Test unhappy paths at the integration level too: DB constraint violations, duplicate keys, connection failures
Security Checklist
Before marking any backend feature complete, verify:
- All inputs validated and sanitized at the API boundary
- SQL/NoSQL injection impossible (parameterized queries / ORM)
- Authentication required on all non-public endpoints
- Authorization checks resource ownership, not just role
- Sensitive data (passwords, tokens, keys) never logged or returned in responses
- Rate limiting applied to auth endpoints and expensive operations
- CORS configured to a whitelist, not
*for credentialed requests - Security headers set on all responses
- Dependencies audited for known vulnerabilities
- Secrets loaded from environment, not source code
Observability
Every backend service must be observable from day one:
- Structured logging — every log line is a machine-readable JSON object with:
timestamp,level,message,correlationId,service, and relevant context fields - Correlation IDs — generate a unique ID per request at the entry point; thread it through every log line, every downstream call
- Log levels used correctly:
DEBUGfor dev-only detail,INFOfor significant state changes,WARNfor recoverable issues,ERRORfor failures that need attention - Health endpoints — every service exposes
/health(liveness) and/ready(readiness) with no auth required - Key metrics to instrument: request count, error rate, latency (p50/p95/p99), DB query time, external call duration
- Never log PII — mask emails, phone numbers, card numbers before they reach any log line
Environment & Configuration
- Every environment variable required at startup must be listed in
.env.examplewith a description comment - Validate all required env vars on startup — if one is missing, crash immediately with a clear error naming the missing variable
- Separate config by environment:
development,test,staging,production - Never use production credentials in development or test environments
- Feature flags for anything that needs to be toggled without a deploy
Docker & Local Dev
Every backend project ships with:
Dockerfile (multi-stage):
- Stage 1: install dependencies
- Stage 2: build / compile
- Stage 3: minimal runtime image, non-root user, only production artifacts
docker-compose.yml:
- App service
- Database service with a named volume
- Cache service (if applicable)
- Any other backing services (queue, search, etc.)
- Health checks on all services
- Environment variables loaded from
.env
Definition of Done
A backend feature is not done until:
- Folder structure follows the project convention
- All inputs validated at the API boundary
- Business logic isolated in the service layer
- Data access isolated in the repository layer
- All error cases handled and logged
- Unit tests written for all service and utility functions
- Integration test written for the critical path
- Security checklist passed
- OpenAPI / schema definition updated
-
.env.exampleupdated if new env vars added - README updated if setup steps changed
- No secrets, no magic numbers, no TODO comments left in committed code