reviewers read for style and shape; behaviour is only checked by tests and by running the thing
The short answer: a code review checks how a change is written, not whether it does the right thing. Most of the attention during review goes to naming, structure, formatting, and the kind of off-by-one slips that jump out when you read the code. Whether the change actually behaves as intended is a separate question, and reviews are not designed to answer it.
Reading code is a pattern-matching task. A reviewer compares each line against a mental model of what the code looks like, not against the state of the program at runtime. A condition such as if (count <= MAX) instead of if (count < MAX) reads cleanly, sits in the same place on the page, and uses familiar operators. Nothing about its shape signals a problem, because the bug is in the relationship between that expression and the surrounding logic. The branch it routes to is also perfectly readable.
This is the core problem. The reviewer is not running the code, so they cannot observe which branch executes for which input. They can only notice when a line looks wrong. Most defects look like surrounding code, so they pass.
A test pins the code to a specification by exercising a path with a chosen input and asserting a concrete output. When the assertion fails, the failure points to a specific behavior, not a stylistic impression. That is why a single well-written test often catches more than several rounds of review: the test forces a comparison the reviewer cannot perform mentally.
Notice the order matters. If tests exist first, the change is required to make them pass, so behavior is verified at the moment of writing. If tests arrive after the merge, the code is already in production and the team is searching for them rather than authoring them.
The most common assumption is that approval equals correctness, especially when the approver is senior. Senior reviewers are better at spotting design problems and naming issues, which are exactly the things review catches well. That makes their approval feel like a stronger signal than it is. A senior reader still cannot run the code, so a logical error that reads cleanly is just as invisible to them as to anyone else.
There are cases where review alone is reasonable. Trivial changes, such as a typo in a comment, a renamed variable, or a formatting fix, have no behavior to verify. Generated code that is regenerated on every build does not benefit from human review of its content; the generator is the specification. Pure refactors that are covered by an existing test suite can lean on those tests instead of review for correctness, with the reviewer still guarding readability.
For anything that changes behavior, review is a complement, not a substitute. The reliable order is a failing test that describes the intended outcome, code that makes it pass, and a review that polishes the result.
Cram If a colleague approves my pull request, the code must be working, right?
Rep Not necessarily. Most reviewers check shape and style, not whether the code does the right thing. Those are two different questions.
Cram So a clean review does not mean the bug is gone?
Rep Right. Reviewers spend most of their attention on naming, structure, and obvious mistakes. The hard logic sits below the surface.
Cram But they read every line, surely they catch something.
Rep They read for what looks off, not for what a runtime check would catch. A typo in a condition reads cleanly and still routes the wrong branch.
Cram So the missed bugs are not hidden, they look normal?
Rep Exactly. The dangerous code looks like the safe code. Without a test that asserts the behaviour, no human reader is forced to compare it to the expected outcome.
Cram Then what is a code review actually good for?
Rep It is great for catching style, simple typos, and design issues. It is a quality filter, not a correctness proof. That work belongs to tests and to running the thing.
Cram So a clean review plus no tests is a quiet accident?
Rep Yes. The merge happens, the test suite never runs the path, and the bug ships. Reviewers feel reassured because nothing looked wrong.
Cram Then tests are the real safety net?
Rep Tests are what compare the code to a specification. A review is a second pair of eyes, a test is an automatic check that the right thing happens on every run.
Cram So I want both, in that order: tests first, review second?
Rep That is the safer order. Tests describe the behaviour, the review polishes the shape. Without tests, the review is reading a story with no ending.