Code Review Isn't a Bug Filter
Treating pull requests as a quality gate blocks flow and misses the point; the real payoff is shared knowledge and a code-health ratchet.
A short post from Mark Jason Dominus arguing that most people misunderstand what code review is for landed on the front page of Hacker News and kicked off the usual argument. Good. It's a debate worth having every couple of years, because the default mental model most teams operate on is wrong, and it quietly makes their process slower and their code worse at the same time.
The default model goes like this: a reviewer is a gate. Code shows up, the reviewer inspects it for defects, and either waves it through or sends it back. Under that framing, review is quality assurance by human eyeball, and a PR that merges with a bug in it is a review failure. This is the model that produces three-day-old pull requests, nitpick wars over brace placement, and the passive-aggressive "LGTM" that means nobody actually read it.
Here's the thesis: catching bugs is the weakest justification for code review, and building your process around it is why review feels like a tax. The durable value is somewhere else entirely, and once you see it, the whole workflow reorganizes around flow instead of friction.
Humans are mediocre bug filters
Review does catch defects. Nobody's disputing that. As one long-running Stack Exchange thread on the topic notes, a careful reviewer will spot something that smells like a bug and then write a test case to prove it. That's real. But it's expensive, inconsistent, and it scales terribly. A reviewer staring at a 400-line diff for fifteen minutes is not going to reason through the concurrency edge case that only shows up under load. Your test suite, your type checker, and your fuzzer will, and they'll do it on every commit forever without getting bored.
The Sourcery team made the sharp version of this point: every checkpoint in your pipeline is a deliberate slowdown with a purpose. If pure velocity were the goal you'd merge straight to main. So the real question isn't "does review find bugs" but "is a human reviewer the right tool for this particular slowdown." For whole categories of issue, the answer is obviously no.
- Style and formatting: linters and formatters, enforced at pre-commit or in CI. A human arguing about tabs is pure waste.
- Duplication and dead code: static analysis flags near-duplicate blocks better than a tired reviewer scanning a diff.
- Obvious correctness: tests and types. If a class of bug can be caught mechanically, it should never reach a person.
Everything you push down to automation is time and goodwill you get back for the parts that actually need a brain.
What review actually buys you
Strip out the mechanical stuff and what's left is the good part. Three things, roughly.
Knowledge transfer. When someone reviews your change, they now know that part of the system exists and roughly how it works. Multiply that across a team and you've lowered your bus factor. This is the point that gets underrated because it doesn't show up in any dashboard. The developer who reviewed the payments refactor last month is the one who can fix it at 2 a.m. without paging you. Review is how understanding of the codebase stops living in one person's head.
Design and shared ownership. Whether a change fits the architecture, whether it invents a fourth way to do something the codebase already does three ways, whether the abstraction will hurt in six months. These are judgement calls that resist automation, and they're the conversations review exists to host. A style guide makes them less combative by giving both people a shared source of truth to point at, which turns "I don't like this" into "this contradicts the pattern we agreed on."
Mentoring. Review is one of the few naturally occurring moments where a senior dev's reasoning gets written down next to concrete code. Posit's tidyverse code review guide treats this as a first-class goal: reviewers are encouraged to leave teaching comments, link to the style guide, and even live-review a PR as a form of pair programming so the author learns what the reviewer looks for. Knowledge doesn't only flow senior-to-junior here, but that's the common direction.
The ratchet, not the gate
The most useful reframe comes out of Google's engineering practices, which the tidyverse principles adapt: the primary purpose of review is to make sure the overall code health of the system improves over time. That single sentence resolves most review dysfunction.
Codebases don't rot in one catastrophic commit. They degrade through a thousand small decreases in code health, technical debt accreting because nobody pushed back. Review is the ratchet that fights the accretion. And the standard that follows is the part most teams get wrong:
reviewers should favor approving a PR once it is in a state where it definitely improves the overall code health of the system being worked on, even if the PR isn't perfect.
There is no perfect code, only better code. A change that makes the system more maintainable shouldn't sit for a week because the reviewer wants one more thing polished. If a comment is just preference, Google's guide recommends prefixing it with "Nit:" so the author knows they can ignore it. That one convention kills more review-induced resentment than any tooling.
Gate thinking says: block until this meets my bar. Ratchet thinking says: does this leave the codebase better than it found it? Ship it and keep the conversation going. The second one moves.
Designing a process that doesn't block flow
Concretely, if you're the person who owns how your team reviews:
- Push everything mechanical below the waterline. Formatter, linter, type check, test suite, dependency and duplication scanners, all in CI and pre-commit. No human comment should ever be about something a machine could have flagged. This is also where automated review tooling, Sourcery and the newer LLM-based reviewers included, earns its keep: as a first pass that clears the noise so the human can spend attention on design.
- Set an SLA on review latency, not on defect count. Aim to respond in hours, not days. A stale PR is a worse outcome than a small imperfection that ships and gets cleaned up later.
- Separate blocking from non-blocking feedback explicitly. "Nit:" and "optional:" prefixes. If everything is blocking, nothing is.
- Keep PRs small. A reviewer can actually reason about 100 lines. At 800 they pattern-match and rubber-stamp, and every benefit above evaporates.
- Measure the right thing. If your review metric is bugs caught, you've reinstalled the gate. The signal you want is whether the team understands the system and whether code health is trending up.
One honest caveat: high-stakes contexts genuinely do want review as a control. Security-sensitive changes, migrations, anything touching money or auth. There the extra scrutiny is the point. But that's a specific, deliberate exception, not the default posture for every one-line change.
The teams that get review right stop asking "did this catch the bug" and start asking "is the codebase healthier and does more than one person understand it now." Reframe it that way and the process stops feeling like a checkpoint you have to clear. It starts feeling like the thing that keeps the whole system knowable, which is the only reason it was ever worth slowing down for.
Sources & further reading
- Many people misunderstand the purpose of code review — mathstodon.xyz
- theory - What is the purpose of a Code Review - Software Engineering Stack Exchange — softwareengineering.stackexchange.com
- Tidyteam code review principles - 1 The purpose of code review — code-review.tidyverse.org
- Why Do We Have Code Reviews Anyway? — sourcery.ai
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 2
need to rethink our code review process