Code Review Standards
Code review is how Node.js tech leads scale quality without reviewing every line forever. These standards prioritize async safety, SQL injection, log PII, and pool lifecycle - the bugs that become SEV2 incidents.
Recipe
Quick-reference recipe card - copy-paste ready.
## Node PR Review Checklist
### Blockers (must fix)
- [ ] No floating promises (`@typescript-eslint/no-floating-promises`)
- [ ] DB queries parameterized (no string concat SQL)
- [ ] No secrets, tokens, or PII in logs
- [ ] `try/finally` or `using` for acquired connections/streams
- [ ] Migrations backward-compatible with N-1 deploy
- [ ] AuthZ check on every mutating route
### Should fix
- [ ] Timeouts on outbound HTTP (`AbortSignal.timeout`)
- [ ] Rate limit on public endpoints
- [ ] Tests for error paths, not only happy path
### Nits (optional)
- [ ] Naming, comment clarityWhen to reach for this:
- Onboarding reviewers to a Node squad
- Post-mortem action: "add review gate for X"
- Setting org-wide standards in Coding Standards
Working Example
// BLOCK: floating promise - handler returns before work completes
app.get("/bad", (req, res) => {
fetchData().then((d) => res.json(d)); // no .catch
});
// OK: async handler with explicit error propagation (Express 5 passes to error middleware)
app.get("/good", async (req, res) => {
const data = await fetchData();
res.json(data);
});// BLOCK: SQL injection
const id = req.params.id;
await pool.query(`SELECT * FROM users WHERE id = ${id}`);
// OK: parameterized
await pool.query("SELECT * FROM users WHERE id = $1", [id]);// BLOCK: PII in logs
req.log.info({ email: user.email, ssn: user.ssn });
// OK: redacted identifiers
req.log.info({ userId: user.id, action: "login" });// BLOCK: connection leak on error path
const client = await pool.connect();
const result = await client.query("SELECT 1");
client.release();
// OK: always release
const client = await pool.connect();
try {
return await client.query("SELECT 1");
} finally {
client.release();
}What this demonstrates:
- Four incident classes caught in review: async, injection, PII, pools
- Express 5 async errors reach error middleware; still prefer explicit
try/catchfor domain errors
Deep Dive
Async Safety
| Smell | Risk | Fix |
|---|---|---|
.then() without .catch() | Unhandled rejection | async/await + lint rule |
forEach with async callback | Fire-and-forget | for...of with await |
Missing await on Prisma call | Race, wrong response | ESLint @typescript-eslint/return-await |
| Event emitter without error handler | Process crash | emitter.on("error", ...) |
Security Checks
- SSRF: validate outbound URLs (see SSRF Guards)
- Prototype pollution: safe
mergeon user objects - JWT: verify alg, exp, issuer; no sensitive data in payload
- Mass assignment: Zod/class-validator whitelist on body
Log PII
const REDACT = ["password", "token", "email", "ssn", "authorization"];
// Use pino redact paths: ['req.headers.authorization', 'body.password']- GDPR: logging email may need lawful basis - default to
userId
Migration Review
[ ] Expand step only in this PR?
[ ] Down migration exists OR forward-fix documented?
[ ] Index CONCURRENTLY for large tables?
[ ] Worker job payload still compatible?Review SLAs
| Size | First response | Approval target |
|---|---|---|
| Small (< 200 LOC) | 4 business hours | 24 hours |
| Medium | 1 business day | 2 days |
| Large | Schedule pairing | Split PR |
Gotchas
- Style debates in review - Wastes senior time. Fix: Prettier + ESLint in CI; humans on blockers only.
- Rubber stamp - LGTM without reading async paths. Fix: Rotate reviewers; checklist in PR template.
- Blocking on nits - Morale drop. Fix: Prefix
nit:in comments. - No tests on bug fix - Regression returns. Fix: Require test reproducing incident.
- Reviewing generated code - Prisma client churn. Fix: Exclude from diff or auto-approve paths.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Pair programming | Complex migration | Trivial typo |
| AI review bot | First pass on security | Final approval without human |
| CODEOWNERS | Platform files | Every file slows squad |
FAQs
How many reviewers?
One required approver; two for migrations and auth changes. Platform CODEOWNERS for shared libs.
Should juniors review seniors?
Yes for learning and diversity of bugs caught. Senior approver still required for merge.
Fastify schema in review?
Verify response schema matches actual return shape - prevents serialization surprises.
Related
- Coding Standards - enforceable lint rules
- Linting & Formatting Best Practices - automation
- PII Redaction and Compliance - log policy
- Post-Mortem Template - turn incidents into checklist items
- Technical Leadership Best Practices - leadership checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.