| name | code-review-standards |
| description | Comprehensive code review standards covering security, quality, performance, testing, and documentation. Includes checklists, common issues, and best practices for thorough code reviews. |
| triggers | ["code review standards","review checklist","security review","code quality review","performance review","what to look for in code review","code review best practices","review criteria","quality gates"] |
Code Review Standards
Master comprehensive code review practices that catch critical issues before they reach production. This skill covers security vulnerabilities, code quality metrics, performance optimization, testing requirements, and documentation standards to ensure every pull request meets professional engineering standards.
Introduction
Code review is your last line of defense against bugs, security vulnerabilities, and technical debt. A thorough review process prevents production incidents, maintains code quality, and transfers knowledge across the team.
Review Philosophy:
- Behavior over implementation - Focus on what the code does, not just how it's written
- Security-first mindset - Always check for vulnerabilities before code quality
- Constructive feedback - Explain the "why" behind every comment
- Question assumptions - If something isn't clear, ask before approving
When to Review vs. Auto-Approve:
- ✅ Always review: Security changes, authentication, data handling, database migrations, API changes
- ✅ Always review: Complex business logic, performance-critical code, public APIs
- ⚠️ Light review: Documentation updates, simple typo fixes, dependency updates (check changelogs)
- ❌ Never auto-approve: Anything you don't understand - ask questions instead
Security Review Checklist
Security vulnerabilities are the highest priority in code review. Check these categories systematically.
For comprehensive security review guidance with examples, see:
Authentication & Authorization Quick Check
What to Check:
- JWT validation and token expiry handling
- Session management and secure cookie configuration
- RBAC (Role-Based Access Control) enforcement
- Password hashing and credential storage (bcrypt, argon2)
- OAuth/SSO implementation correctness
Review Questions:
- Are all protected routes behind authentication middleware?
- Does the code check user permissions before sensitive operations?
- Are tokens validated on every request (not just cached)?
- Is password reset flow secure (tokens, expiry, rate limiting)?
- Are credentials never hardcoded in code?
Input Validation & Sanitization Quick Check
What to Check:
- SQL injection prevention (parameterized queries only)
- XSS prevention (proper output encoding)
- Command injection prevention (use execFile, not shell commands)
- Path traversal prevention
- File upload validation (type, size, content scanning)
Review Questions:
- Are all database queries parameterized (no string concatenation)?
- Is user input validated against a whitelist (not just blacklist)?
- Are file uploads restricted by type, size, and scanned for malware?
- Is output properly escaped for the context (HTML, URL, JS)?
- Are shell commands avoided or properly sanitized?
Data Protection Quick Check
What to Check:
- Encryption at rest (sensitive data in database)
- Encryption in transit (HTTPS, TLS)
- PII (Personally Identifiable Information) handling
- Secrets management (environment variables, never hardcoded)
- Data retention and deletion policies
Review Questions:
- Is sensitive data encrypted both at rest and in transit?
- Are PII fields properly identified and protected (GDPR compliance)?
- Are secrets loaded from environment variables (never committed to git)?
- Does the code comply with data retention policies?
- Is sensitive data never logged?
OWASP Top 10 Quick Reference
For comprehensive security review, verify the code doesn't have these OWASP vulnerabilities:
- Broken Access Control - Check authorization on every protected resource
- Cryptographic Failures - Verify strong encryption, no weak algorithms
- Injection - SQL, NoSQL, command, LDAP injection prevention
- Insecure Design - Threat modeling, secure architecture
- Security Misconfiguration - Check default configs, error messages don't leak info
- Vulnerable Components - Check dependency versions, known CVEs
- Authentication Failures - MFA, credential stuffing prevention, weak password policies
- Software/Data Integrity - CI/CD security, unsigned packages
- Logging/Monitoring Failures - Security events logged, alerting configured
- SSRF - Server-Side Request Forgery prevention
See references/security-review.md for detailed examples and remediation patterns.
Code Quality Standards
Beyond security, code must be maintainable, readable, and follow best practices.
Complexity Metrics
Cyclomatic Complexity: Aim for functions with complexity < 10. High complexity (>15) is a code smell.
❌ Bad: High complexity (15+ branches)
function processOrder(order, user, inventory) {
if (order.type === 'standard') {
if (user.isPremium) {
if (inventory.stock > 0) {
if (order.quantity < inventory.stock) {
if (user.paymentMethod === 'card') {
}
}
}
}
}
}
✅ Good: Extracted functions (complexity < 10 each)
function processOrder(order, user, inventory) {
validateOrder(order);
checkInventory(order, inventory);
processPayment(order, user);
updateStock(order, inventory);
}
function validateOrder(order) {
if (!order.type || !order.quantity) {
throw new ValidationError("Invalid order");
}
}
Review Questions:
- Are functions short and focused (< 50 lines ideal)?
- Is nesting depth reasonable (< 4 levels)?
- Can complex functions be broken into smaller ones?
- Are early returns used to reduce nesting?
Code Smells
Common Smells to Flag:
❌ Duplicate Code: Same logic repeated in multiple places
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
✅ Good: Extracted to shared utility
export function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
❌ Magic Numbers: Unexplained constants
if (user.age > 65) { ... }
setTimeout(pollAPI, 5000);
✅ Good: Named constants
const RETIREMENT_AGE = 65;
const API_POLL_INTERVAL_MS = 5000;
if (user.age > RETIREMENT_AGE) { ... }
setTimeout(pollAPI, API_POLL_INTERVAL_MS);
❌ Long Parameter Lists: > 4 parameters is a smell
function createUser(name, email, password, age, country, city, zipCode) { ... }
✅ Good: Object parameter
function createUser({ name, email, password, profile }: CreateUserParams) { ... }
❌ God Objects/Functions: Classes/functions doing too much
class UserService {
authenticate() { ... }
sendEmail() { ... }
processPayment() { ... }
generateReport() { ... }
}
✅ Good: Single Responsibility Principle
class AuthService { authenticate() { ... } }
class EmailService { send() { ... } }
class PaymentService { process() { ... } }
class ReportService { generate() { ... } }
Naming Conventions
Variables and Functions:
- Use descriptive names (avoid abbreviations unless universally known)
- Boolean variables should be questions:
isValid, hasPermission, canEdit
- Functions should be verbs:
getUserById, calculateTotal, validateEmail
❌ Bad: Vague names
const d = new Date();
function proc(x) { ... }
const temp = fetchData();
✅ Good: Descriptive names
const currentDate = new Date();
function processPayment(order) { ... }
const userData = await fetchUserProfile();
Code Organization (SOLID Principles)
Single Responsibility: Each class/function should have one reason to change
Open/Closed: Open for extension, closed for modification (use composition/inheritance)
Liskov Substitution: Subtypes should be substitutable for base types
Interface Segregation: Many specific interfaces > one general interface
Dependency Inversion: Depend on abstractions, not concretions
Review Questions:
- Does each function/class have a single, clear purpose?
- Can the design be extended without modifying existing code?
- Are dependencies injected (not hardcoded)?
See examples/code-quality-examples.md for 15-20 refactoring examples.
Performance Considerations
Look for performance issues that could cause production problems under load.
Database Optimization
N+1 Query Problem: Most common performance issue
❌ Bad: N+1 queries (1 for posts + N for authors)
const posts = await db.query('SELECT * FROM posts');
for (const post of posts) {
post.author = await db.query('SELECT * FROM users WHERE id = ?', [post.authorId]);
}
✅ Good: Single query with JOIN
const posts = await db.query(`
SELECT p.*, u.name as authorName, u.email as authorEmail
FROM posts p
JOIN users u ON p.authorId = u.id
`);
Missing Indexes: Check for queries on unindexed columns
❌ Bad: Full table scan
SELECT * FROM orders WHERE customer_email = 'user@example.com';
✅ Good: Indexed column
CREATE INDEX idx_orders_email ON orders(customer_email);
SELECT * FROM orders WHERE customer_email = 'user@example.com';
Large Result Sets: Always paginate
❌ Bad: Returning all results
const allUsers = await db.query('SELECT * FROM users');
✅ Good: Pagination
const users = await db.query(
'SELECT * FROM users LIMIT ? OFFSET ?',
[pageSize, page * pageSize]
);
See references/performance-review.md for comprehensive database optimization patterns.
Frontend Performance
Unnecessary Re-renders:
❌ Bad: Creating new objects in render
function Component() {
const options = { key: 'value' };
return <Child options={options} />;
}
✅ Good: Memoization
function Component() {
const options = useMemo(() => ({ key: 'value' }), []);
return <Child options={options} />;
}
Bundle Size: Check for large dependencies
❌ Bad: Importing entire libraries
import _ from 'lodash';
import moment from 'moment';
✅ Good: Tree-shakeable imports
import debounce from 'lodash/debounce';
import { formatDistance } from 'date-fns';
API Design
Efficient Data Transfer:
❌ Bad: No pagination, overfetching
app.get('/api/products', async (req, res) => {
const products = await db.getAllProducts();
res.json(products);
});
✅ Good: Pagination + selective fields
app.get('/api/products', async (req, res) => {
const { page = 1, limit = 20, fields = 'id,name,price' } = req.query;
const products = await db.getProducts({ page, limit, fields });
res.json(products);
});
Review Questions:
- Are database queries optimized (indexes, JOINs instead of loops)?
- Is pagination implemented for list endpoints?
- Are large computations cached or memoized?
- Is lazy loading used for images and code?
Testing Requirements
Code review should verify comprehensive test coverage.
Coverage Thresholds
Minimum Coverage Targets:
- Critical paths (auth, payments, data integrity): 100%
- Business logic: 90%+
- UI components: 70-80%
- Utilities/helpers: 85%+
Coverage is Necessary but Not Sufficient: 100% coverage doesn't guarantee bug-free code. Test quality matters more than quantity.
Test Quality (AAA Pattern)
Good tests follow Arrange, Act, Assert:
❌ Bad: Testing implementation details
test('counter increments', () => {
const counter = new Counter();
counter.value = 5;
expect(counter.value).toBe(5);
});
✅ Good: Testing behavior
test('counter increments when button clicked', () => {
const counter = new Counter();
counter.increment();
counter.increment();
expect(counter.getValue()).toBe(2);
});
❌ Bad: Multiple assertions without clear meaning
test('user creation', async () => {
const user = await createUser({ name: 'John' });
expect(user).toBeDefined();
expect(user.id).toBeTruthy();
expect(user.name).toBe('John');
expect(user.createdAt).toBeTruthy();
});
✅ Good: Focused tests with clear intent
test('createUser assigns unique ID to new user', async () => {
const user = await createUser({ name: 'John' });
expect(user.id).toMatch(/^[a-f0-9-]{36}$/);
});
test('createUser sets createdAt timestamp', async () => {
const before = Date.now();
const user = await createUser({ name: 'John' });
const after = Date.now();
expect(user.createdAt).toBeGreaterThanOrEqual(before);
expect(user.createdAt).toBeLessThanOrEqual(after);
});
Test Types Required
When to Require Each Type:
Unit Tests (always required):
- All business logic functions
- Utilities and helpers
- Data validation
- Calculations and transformations
Integration Tests (required for):
- API endpoints
- Database operations
- External service integrations
- Multi-component workflows
E2E Tests (required for):
- Critical user flows (signup, checkout, payments)
- Complex multi-page workflows
- Features involving multiple services
Review Questions:
- Are all new functions covered by tests?
- Do tests cover edge cases and error conditions?
- Are tests focused on behavior, not implementation?
- Do integration tests verify API contracts?
See references/test-review.md for comprehensive testing guidance.
Documentation Expectations
Documentation should explain why, not just what.
When Documentation is Required
Always Document:
- Public APIs (exported functions, classes, endpoints)
- Complex algorithms or business logic
- Non-obvious design decisions
- Security considerations
- Performance optimizations
Optional Documentation:
- Self-explanatory code (good naming makes comments redundant)
- Obvious implementations (standard CRUD operations)
What to Document
Function/Method Documentation:
❌ Bad: Redundant comments
function getUserById(id) { ... }
✅ Good: Meaningful documentation
async function getUserById(id: string): Promise<User | null> { ... }
Complex Logic Documentation:
❌ Bad: No explanation
const price = base * (1 + tax) * (1 - discount) + shipping;
✅ Good: Explained calculation
const price = base * (1 + tax) * (1 - discount) + shipping;
Documentation Formats
Use appropriate format for context:
- JSDoc/TSDoc: For TypeScript/JavaScript functions and classes
- Inline comments: For complex logic within functions
- README/docs: For architecture, setup, API guides
Review Questions:
- Are public APIs documented with parameters, returns, and examples?
- Are complex algorithms explained (the "why", not just "what")?
- Do comments add value (not just restate the code)?
Review Process Best Practices
Review Etiquette
Be Constructive:
- ✅ "Consider extracting this to a helper function for reusability"
- ❌ "This code is terrible"
Ask Questions:
- ✅ "Why did you choose approach X over Y?"
- ❌ "This is wrong" (without explanation)
Explain Reasoning:
- ✅ "Parameterized queries prevent SQL injection (OWASP #3)"
- ❌ "Use parameterized queries" (without context)
Acknowledge Good Work:
- ✅ "Great test coverage on this edge case!"
- ✅ "Nice refactoring, much clearer now"
See examples/review-comments.md for template review comments.
Common Review Comments
Security:
- "🔒 This endpoint needs authentication middleware"
- "🔒 User input should be validated before database query"
- "🔒 Sensitive data should not be logged"
Code Quality:
- "♻️ Consider extracting this logic to a reusable function"
- "♻️ Magic number - define as named constant"
- "♻️ Could simplify with early return"
Performance:
- "⚡ N+1 query detected - use JOIN or eager loading"
- "⚡ This could be memoized to avoid recalculation"
- "⚡ Consider pagination for large result sets"
Testing:
- "🧪 Missing test case for error condition"
- "🧪 This test is brittle - testing implementation, not behavior"
- "🧪 Integration test needed for this API endpoint"
When to Approve vs. Request Changes
✅ Approve:
- All critical issues resolved (security, correctness)
- Minor nitpicks noted but don't block (style preferences)
- Tests pass and coverage is adequate
- Documentation is sufficient
🔄 Request Changes:
- Security vulnerabilities present
- Tests missing or failing
- Critical bugs or logic errors
- Performance issues that could cause production problems
💬 Comment Only (no approval/rejection):
- Suggesting improvements but not blocking
- Asking questions for clarification
- Sharing knowledge or context
Quick Reference
Security Checklist
Code Quality Checklist
Performance Checklist
Testing Checklist
Documentation Checklist
Related Skills
testing-strategy - Test pyramid and comprehensive testing patterns
github-workflow-best-practices - PR workflow and review process
agency-workflow-patterns - Multi-agent code review coordination (Reality Checker agent)