What Is a Code Review? (Best Practices Guide)
A code review is a systematic examination of a change by someone other than the author — usually on a pull request — before it merges. Here is why it works, what reviewers should look for, and the habits that make reviews catch bugs instead of rubber-stamping them.
Almost every team that ships software has the same rule: nobody merges their own code alone. Before a change lands in the shared codebase, someone else reads it. That practice has a name — code review — and it is one of the highest-leverage habits a team can have. It is also one of the easiest to do badly, which is why so many reviews devolve into a rubber-stamp "LGTM" or a fight over where the braces go.
This guide explains what a code review actually is, why it works, what a reviewer should be looking at, and the concrete habits — for both authors and reviewers — that separate reviews that catch bugs from reviews that just add latency.
What a code review is
A code review is a systematic examination of a proposed code change by someone other than its author, done before the change is merged — almost always on a pull or merge request. Reviewers read the diff, leave inline comments, and approve or request changes to catch defects, improve quality, and share knowledge.
The modern shape of this is the pull request (GitLab calls it a merge request): the author pushes a branch, opens a request to merge it, and one or more reviewers read the change line by line. They leave inline comments, ask questions, suggest alternatives, and eventually either approve it or request changes. Only after approval does the code merge into the main line.
It is worth being precise about the word systematic. Code review is not "glancing at someone's screen" — it is a defined step in the workflow with a defined outcome. Reviews range from heavyweight formal inspections (structured meetings with roles, the classic Fagan inspection) to the lightweight, tool-assisted, asynchronous review most teams run today. The lightweight kind wins on cost-effectiveness, which is why it has become the default.
Why it works
Code review earns its place because it pays off in four different ways at once:
- It catches bugs early. A defect caught in review is fixed by editing a diff; the same defect caught in production costs an incident, a hotfix, and a customer's trust. Pushing detection earlier — the idea behind shift-left testing — is dramatically cheaper, and review is one of the earliest gates you have.
- It spreads knowledge. When a second person reads every change, no part of the system stays known to only one engineer. That shrinks your bus factor and makes the whole team more resilient.
- It keeps the codebase consistent. Reviewers steer changes toward existing patterns and conventions, so the system stays coherent instead of becoming ten personal dialects stapled together.
- It mentors. Review is where junior engineers learn how the senior ones think, and where seniors learn from fresh eyes. The feedback loop teaches in both directions.
What reviewers should look for
The most important thing a reviewer can do is spend their attention where a machine can't. A human reviewer should be checking:
- Correctness and logic. Does the code actually do what the change claims? Are there off-by-one errors, inverted conditions, or wrong assumptions?
- Edge cases. What happens on empty input, nulls, concurrent access, the maximum value, the network failure? Bugs cluster at boundaries.
- Tests. Are there tests at all, and do they actually cover the new behavior — including the failure paths — rather than just asserting the happy case?
- Readability and maintainability. Will the next person understand this in six months? Are names clear, is the structure sane?
- Security. Unvalidated input, injection, leaked secrets, missing authorization checks.
- Performance. Accidental N+1 queries, unbounded loops, work that doesn't need to happen on the hot path.
- Conventions. Does it fit the team's architecture and idioms?
Notice what is not on that list: style nits. Whether a string uses single or double quotes, whether there is a trailing comma, how a block is indented — none of that should ever consume a reviewer's attention, because a linter and formatter should settle it automatically before a human ever looks. Google's engineering practices make this explicit: reviewers approve changes that improve the overall health of the codebase, and they let tooling own the mechanical rules so people can focus on the judgment calls machines can't make.
Best practices for authors
A good review starts before a reviewer ever sees the change. The author controls most of what makes a review fast and effective:
- Keep the PR small and focused. One logical change per pull request. A small diff gets read carefully; a giant one gets skimmed.
- Write a clear description — and the why. Say what the change does and, more importantly, why it exists and what alternatives you rejected. Link the ticket or the acceptance criteria it satisfies so the reviewer can judge whether it actually does the job.
- Self-review first. Read your own diff before requesting review. You will catch the debug print, the stray TODO, and the half-finished rename yourself — and spare a human from doing it.
- Include tests. Ship the change and the tests that prove it works together. "Tests to follow" almost never follow.
Best practices for reviewers
Reviewers set the tone. The difference between a team that loves review and one that dreads it lives almost entirely in reviewer behavior:
- Review promptly. A pull request that sits for two days blocks the author and invites them to context-switch away. Fast reviews keep everyone moving.
- Be specific and be kind. Comment on the code, not the person. Explain the reasoning behind a request so the author learns, rather than just complies.
- Distinguish blocking issues from suggestions. Prefix optional feedback with
nit:so the author knows what must change versus what is merely a preference. A wall of equally-weighted comments is impossible to act on. - Ask questions instead of issuing demands. "What happens here if
itemsis empty?" invites a conversation; "this is wrong, fix it" invites defensiveness — and you might be the one who misread. - Approve when it is good enough, not perfect. The bar is whether the change improves the codebase, not whether it is exactly how you would have written it. Endless polishing is its own anti-pattern.
The single most consequential move a reviewer makes is in how they phrase feedback. Compare these two comments on the same line of code:
function totalPrice(items) {
- return items.reduce((sum, i) => sum + i.price, 0);
}
# Vague, unhelpful, a little hostile:
# "this is broken"
# Specific, kind, and actionable:
# "nit-adjacent, but a real one: if `items` is empty this returns 0,
# which is correct — but if any item is missing `price` we'd get NaN
# silently. Should we guard against a missing price, or is the input
# guaranteed upstream? A test for the empty and missing-price cases
# would lock this down."Both comments took roughly the same number of seconds to type. The first tells the author nothing and starts a fight; the second names the exact scenario, explains the risk, asks rather than demands, and points at the fix. That is the whole craft of reviewing in one diff.
Let the machines go first
The most effective review pipelines put automation ahead of humans. A change should not reach a human reviewer until the linter, the formatter, the type checker, and the test suite have already run and passed in CI/CD. That first automated pass strips out the entire class of mechanical problems — formatting, obvious type errors, broken tests — so the human is free to spend their scarce attention on logic, design, and edge cases, which is the only thing they can do that a machine can't. When CI is green before review starts, reviews get faster and sharper at the same time.
Common anti-patterns
The honest limit
Here is the part most guides skip: code review catches a lot, but it does not catch everything. A reviewer reads the diff — they do not run the system with real data, real concurrency, and real users doing things nobody imagined. Studies going back decades, including the classic Cisco/SmartBear work, show review is remarkably cost-effective at finding defects, but no method finds them all. Bugs still slip past a clean review and reach production.
Which is exactly why review is one layer, not the whole strategy. It sits alongside automated tests, CI, and staged rollouts — and behind all of them, a way to deal with the bugs that make it through anyway. Review reduces the defects that reach your users; it does not eliminate them. The ones that get through still need to be reproduced and fixed fast, and the single biggest determinant of fix speed is whether the person fixing it can see the failure instead of guessing at it. That is where a captured reproduction — a session replay plus the console and network state from the exact moment it broke, which is what BugMojo grabs in one click — turns "it worked in review" into a fixable ticket without anyone reconstructing the failure from memory.
Even a great review lets some defects reach production. When one does, capture the reproduction in one click — a session replay plus console and network, ready for a developer or an AI agent over MCP.
Install the free extensionFrequently asked questions
Frequently asked questions
Sources
- Google Engineering Practices — the code review developer guide, including 'the standard of code review' and the case for small changes — Google Engineering Practices (2026)
- Martin Fowler — 'Pull Request' and the mechanics and trade-offs of pre-integration review on branches — martinfowler.com (2026)
- SmartBear — best practices for peer code review, drawing on the Cisco/SmartBear study of review size and effectiveness — SmartBear (2026)
- Wikipedia — code review: definition, history, and the varieties of formal and lightweight review — Wikipedia (2026)
Get bug-tracking insights, weekly.
Engineering deep-dives, QA playbooks, and honest tool comparisons. No spam — unsubscribe in one click.

