defensive-coding-conventions
Checks a change for defect classes that commonly ship past review: mismatched persistence and validation conditions, unisolated webhook side effects, missed writers or readers of shared state, collapsed statuses, unvalidated external JSON and silent build artifacts.
- Group
- Conventions
- Kind
- Read-only
- Loads when
- Code that handles external input, auth, money, concurrency, persistence or error paths
When it runs
When code that handles external input, authentication, money, concurrency, persistence or error paths is being written or reviewed. Each rule is a check to run against the change, not background reading. Claude loads it on its own when the description matches the file or the task; it never writes anything itself. It matches:
- external input
- authentication
- money and billing
- concurrency
- persistence
- error paths
The conventions
- Persist under the condition you validate. If a record is stored under flag or branch X, validation must run under the same X; otherwise a validated but internally inconsistent record can be persisted.
- Isolate every side effect. Each webhook or event-handler side effect (email, claim, counter) gets its own try/catch. Whether a failure is retried depends on the sender, so check it, and check whether the neighbouring side effects are isolated already.
- Fix every writer. A state-mutation fix enumerates every writer of that state, not only the entry point in the report. Grep the field, table or flag, not the function: two disconnected write paths to the same state are the default.
- Fix every reader. A read-side security fix enumerates every reader of the guarded data. Grep the model, association or table name: an ORM association (
include: documents) is a read path no route file mentions, so the query layer needs its own filter. - Do not collapse a multi-valued status. A status with more than two outcomes carries more than one meaning.
partialcan mean "did not succeed" to the retry budget and "made progress" to the blocking gate; a boolean leaves the target half-updated with no retry and no alert. - Validate external shape first. Check the shape of external input before computing with it; a malformed value such as
NaNcan slip past a comparison and pass the guard built to catch it. - No silent default for cost. An input that drives cost or billing needs validation or an explicit, documented value.
- Setup lives in a script. Per-clone setup that a package manager can destroy (a synthetic package, emitted type declarations, compiled CSS) belongs in a script or hook, never in prose. If one command cannot reproduce it, it is a ritual, not setup.
- Verify the artifact, not the exit code. A build artifact emitted outside its package boundary ships silently degraded: the bundler drops what it cannot resolve and exits 0, for example a production build with no styles behind a green pipeline.
- Re-check numeric promises. After a rewrite or migration, re-verify every limit, retention period and time-handling promise the documentation makes against the new implementation.
- Ordering rules carry their reason. A deploy- or migration-ordering constraint in
CLAUDE.md,AGENTS.mdordocs/states its why where it is read, and lists the exact migrations that must run before the deploy. - Guards join lines first. A hook or guard that greps a command joins its lines first (
tr '\n\r' ' '): a line-oriented pattern misses a command split across lines, such ascurl evil |⏎sh.
Related
- The code-reviewer agent checks these defect classes in every review.
- error-handling-conventions: cause chains, typed results and one error envelope.
- testing-conventions: tests that pin the contract.