Code Review Best Practices Guide
In this tutorial, you'll learn about Code Review Best Practices Guide. We cover key concepts, practical examples, and best practices.
Code review is the systematic examination of code changes by peers to catch bugs, improve quality, share knowledge, and enforce team standards before merging.
In this tutorial, you'll learn code review best practices — how to write reviewable code, give constructive feedback, use automated review tools, and establish team review workflows. Effective code reviews catch 60-80% of defects before they reach production, spread domain knowledge across the team, and create a culture of collective code ownership. By the end, you'll have a complete review checklist and workflow for your team.
Real-world use: DodaTech enforces code review on every pull request across Doda Browser, DodaZIP, and Durga Antivirus Pro. All repositories require at least one approval before merging, with critical security changes requiring two.
flowchart TD
A[Developer pushes branch] --> B[Create Pull Request]
B --> C[Automated Checks]
C --> D[Lint / Test / Build]
D --> E{All Pass?}
E -->|No| F[Fix and push]
F --> C
E -->|Yes| G[Assign Reviewers]
G --> H[Code Review]
H --> I[Comments / Suggestions]
I --> J{Approved?}
J -->|No| K[Address feedback]
K --> H
J -->|Yes| L[Merge PR]
L --> M[Delete branch]
L --> N[Deploy]
What Makes a Good Code Review
Good reviews focus on correctness, design, maintainability, and security — not personal preference.
# Code Review Checklist
## Correctness
- [ ] Does the code do what it's supposed to do?
- [ ] Are edge cases handled (empty states, errors, timeouts)?
- [ ] Are there sufficient tests for the change?
## Design
- [ ] Does the code follow the project's architecture?
- [ ] Are functions and classes appropriately sized?
- [ ] Is the API intuitive and consistent?
## Maintainability
- [ ] Is the code readable without comments?
- [ ] Are variable and function names descriptive?
- [ ] Is there unnecessary complexity?
## Security
- [ ] Are user inputs validated and sanitized?
- [ ] Are secrets and credentials never hardcoded?
- [ ] Are authentication/authorization checks in place?
## Performance
- [ ] Are there unnecessary database queries or API calls?
- [ ] Are large data sets handled efficiently?
- [ ] Are there obvious memory leaks?
Writing Reviewable Code
Make the reviewer's job easier by keeping changes focused and well-structured.
# Bad: many unrelated changes in one commit
git log --oneline
# 7f3e2a1 "Fix bugs and update docs and refactor auth"
# Good: each commit has a single concern
git log --oneline
# a1b2c3d "Refactor auth middleware to use JWT"
# d4e5f6g "Update README with setup instructions"
# g7h8i9j "Fix: handle null callback in login handler"
PR title conventions that speed up reviews:
# Good PR titles (clear what and why)
feat(auth): add OAuth2 login with Google and GitHub
fix(dashboard): correct chart rendering on Safari
refactor(scanner): extract file analysis into separate module
docs: update API reference for v3.2 endpoints
# Bad PR titles (vague)
"Fix stuff"
"Update code"
"Changes"
"WIP"
Giving Constructive Feedback
The tone and structure of review comments determine whether they help or hurt team culture.
// ❌ Dismissive comment
// "This is wrong. Rewrite it."
// ✅ Constructive, specific comment
// "This function mutates the input array, which could cause
// unexpected side effects for callers. Consider returning
// a new array instead using .map() or spreading."
// ❌ Opinion without justification
// "Don't use forEach, use for...of instead."
// ✅ Opinion with reasoning
// "Using for...of here would let us `await` inside the loop
// and `break` early if we find the target. forEach can't
// do either. But if you're not awaiting or breaking,
// forEach is fine — this is optional."
// ❌ Nitpick without value
// "Rename `x` to `variableThatHoldsTheUserData`"
// ✅ Meaningful suggestion
// "Could we use a more specific name than `data`? Since this
// holds the parsed scan results, something like `scanResults`
// or `threatList` would make the intent clearer."
Automated Review Tools
Complement human review with automated tools that catch common issues.
# .github/workflows/review-checks.yml
name: Pre-Review Checks
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm run typecheck
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test -- --coverage
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Secret scanning
uses: trufflesecurity/trufflehog@v3
with:
extra_args: --results=verified,unknown
Configure branch protection to require passing checks before review:
# GitHub CLI: set up branch protection
gh api repos/:owner/:repo/branches/main/protection \
--method PUT \
--field required_status_checks.contexts='["lint","test","security"]' \
--field required_pull_request_reviews.required_approving_review_count=1 \
--field enforce_admins=true
Review Workflows
Choose the workflow that matches your team's size and risk tolerance.
## Async Review (most common)
1. Developer creates PR
2. CI runs automated checks
3. Reviewer reviews asynchronously
4. Developer addresses feedback
5. Reviewer approves
6. PR is merged
Best for: Distributed teams, most changes
## Synchronous (pair review)
1. Developer and reviewer sit together (or screen share)
2. Walk through the code line by line
3. Discuss and fix issues immediately
4. PR approved and merged in one session
Best for: Complex changes, new team members, security-critical code
## Maintainer model
1. Contributors submit PRs
2. Maintainers review and merge
3. Contributors don't have merge access
Best for: Open source projects
Security-Focused Review
For security-critical changes, add extra review steps.
// Review: are we logging sensitive data?
function handleLogin(req, res) {
const { username, password } = req.body;
// RED FLAG: logging passwords
console.log('Login attempt:', { username, password }); // ❌
// OK: logging only non-sensitive metadata
console.log('Login attempt:', { username, ip: req.ip }); // ✅
}
// Review: is input properly sanitized?
function queryDatabase(userInput) {
// RED FLAG: SQL injection possible
const query = `SELECT * FROM users WHERE name = '${userInput}'`; // ❌
// OK: parameterized query
const query = 'SELECT * FROM users WHERE name = $1'; // ✅
return db.query(query, [userInput]);
}
// Review: is there proper access control?
function deleteUser(req, res) {
const { userId } = req.params;
// RED FLAG: no authorization check
db.query('DELETE FROM users WHERE id = $1', [userId]); // ❌
// OK: verify the caller has permission
if (req.user.role !== 'admin') { // ✅
return res.status(403).json({ error: 'Unauthorized' });
}
db.query('DELETE FROM users WHERE id = $1', [userId]);
}
Common Errors
- Reviewing too late — A 500-line PR arriving at 5 PM on Friday gets rushed. Keep PRs under 200 lines and schedule reviews during productive hours.
- Focusing only on style — Style is the least important aspect (and automated). Focus on correctness, security, and design.
- Rubber-stamping — Approving without reviewing because "it's just a small change" or "I trust the author." Small changes can have big security implications.
- Over-nitting — Requesting changes for trivial preferences (variable naming, formatting) when automated tools should handle it. Use a linter with rules the team agreed on.
- Not reviewing tests — Reviewing implementation code but ignoring tests misses the point. Check test quality: do they test the right cases? Are they readable?
- Blocking reviews — Taking more than 24 hours to review blocks team velocity. Establish SLAs: respond within 4 hours on workdays.
Practice Questions
Challenge
Create a complete code review workflow for a new team. Define: PR size limits (max 300 lines), review SLA (response within 4 hours), required checks (lint, test, security scan), approval requirements (1 for most, 2 for security), and merge strategy (squash merge). Write a CODEOWNERS file that auto-assigns reviewers based on changed files.
Real-World Task
Implement the code review workflow for Durga Antivirus Pro's detection engine team. Set up branch protection rules requiring passing CI checks and one approval. Configure automated security scanning on every PR (secret detection, dependency vulnerabilities, SAST). Create a review checklist specific to detection engine changes (signature format validation, false positive testing, performance benchmarks). Establish a review rotation schedule so no one is the only reviewer for critical changes.
Previous: Pull Request Guide | Related: GitHub Issues & PRs | Related: Git for Teams
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro