The confidently wrong pull request
It had a clear summary, a passing test suite, and a rationale for every decision. It was also wrong in a way that took eight days and a production incident to find.
The pull request was a pleasure to read. Title was descriptive. Description had three paragraphs explaining the problem, the approach, and one alternative that had been considered and rejected with a sensible reason. The diff was 340 lines across six files, logically grouped. There were nine new tests. The reviewer approved it in about four minutes and felt good about it, because reviewing a well-presented change is one of the few genuinely pleasant parts of the job.
Eight days later, currency conversion in the billing exporter was producing amounts that were correct to two decimal places and wrong by a factor that varied by region. The bug was in a helper that rounded before converting rather than after. It was three lines. The tests covered it thoroughly, using fixtures that had been generated from the same incorrect assumption.
Here is the part I keep coming back to. If a junior engineer had opened that pull request, it would have looked different. It would have had a slightly uncertain description. Maybe a comment in the diff saying “not sure if this rounding is right, someone check me.” The test fixtures would have been sparse or copied from somewhere obvious. The reviewer would have read it more slowly, because the presentation would have signalled that slow reading was warranted.
The signal was gone. And the signal was doing an enormous amount of work.
Review was never really about the code
We tell ourselves that code review is a process where a qualified person reads a change and evaluates its correctness. That is the official story. It has never been the whole story, and pretending otherwise is why the current situation is confusing people.
What actually happens in a good review is closer to triage. The reviewer skims, forms a fast impression of how much scrutiny this change deserves, and then allocates their real attention to the parts that seem to need it. That allocation decision is where most of the value is, and it is made in seconds, on the basis of signals that have almost nothing to do with the code itself.
Who wrote it. How they wrote the description. Whether the diff is tidy or chaotic. Whether the tests look thoughtful or perfunctory. Whether the author left themselves an out (“I think this is right but”). Whether the change is in a scary part of the codebase. Whether the author usually gets this kind of thing right.
Every one of those is a proxy for effort and confidence, and every one of them has been decoupled from actual correctness by agentic authoring.
An agent produces a tidy diff regardless. It writes a clear description regardless. It generates plausible tests regardless. It does not hedge, because hedging is not a natural output of the process. So a change that the model essentially guessed at arrives looking exactly like a change it had strong grounds for, and the reviewer’s triage function, which was tuned over a career on human-authored work, silently reads the wrong answer off it.
The reviewer is not being lazy. Their instrument has been miscalibrated without their knowledge, which is a much harder problem than laziness.
Fluency is not a lie, which is what makes it dangerous
I want to be careful not to describe this as deception, because it is not, and framing it that way leads people to the wrong fixes.
A confidently wrong pull request is not a pull request that lies about what it does. Usually the description is accurate. In the billing example, the description said it added a currency conversion helper and used it in the exporter, and it did exactly that. The rationale for the approach was genuinely reasonable. The rejected alternative was genuinely worth rejecting.
The problem is narrower: the description is a faithful account of the change and a completely uninformative account of the change’s reliability. It tells you what was done. It tells you nothing about which parts were derived from strong evidence and which parts were pattern-completed from the shape of similar code elsewhere. Both come out in the same prose, in the same tone, at the same length.
Human authorship bundled those two things together for free. When a developer wrote “I converted then rounded because the finance docs specify banker’s rounding at the boundary,” the sentence carried its own provenance. You could tell they had gone and read something. When they wrote “rounding here seemed right,” you could tell they had not, and that is where you looked.
The fluency is not covering anything up. It is just uniform, and uniformity destroys the information that variation used to carry.
Where the tests stop helping
The reflexive answer is: fine, do not trust the description, trust the tests.
The tests are frequently generated from the same understanding that produced the bug. This is the part that surprises people the first time, and it should not, because it is structurally obvious once stated. If the implementation rounds before converting because the author believed that was correct, the fixture data will encode that belief too. The test will pass. It will pass forever. It is a very well-written test of the wrong behaviour.
This is not unique to agents. Humans write tests that codify their own misunderstandings constantly. The difference is volume and confidence again: a human writing a test they are unsure about tends to write two or three cases and feel uneasy. Generated test suites tend to be thorough, which reads as diligence, and a thorough test suite around a wrong assumption is more convincing than a thin one.
So “the tests pass” has quietly changed meaning. It used to mean roughly “someone thought about the edge cases and encoded them.” It now means “the implementation is self-consistent with its author’s model of the problem.” Those are extremely different claims and they look identical on the pull request page.
What the green check now means
- The implementation is self-consistent
- Tests derived from the same understanding
- A misunderstanding is asserted, not caught
- Coverage rises, detection does not
Tests that kept their old meaning
- Written by someone other than the author
- Written before the change
- Derived from the requirement, not the code
- Able to disagree with the implementation
The tests that still carry their old meaning are the ones written by someone other than the change’s author, before the change, from the requirement rather than from the implementation. Which is a very old idea, and it is having a quiet renaissance for exactly this reason.
What reviewers should actually do differently
I am wary of advice here because most of it degrades into “review more carefully,” which is not advice, it is a wish.
The reframe that I think holds up is this: stop reviewing the change and start reviewing the claim.
Concretely, a reviewer’s first question should not be “is this code good” but “what would have to be true for this to be correct, and did anyone check that thing.” In the billing case, the load-bearing fact was the ordering of rounding and conversion for the currencies in scope. That is one question. It is answerable in ten minutes with a document or a domain expert. Everything else in that 340-line diff was, in retrospect, irrelevant to whether it worked.
Most changes have between one and three such facts. Finding them is a different skill than reading code, and it is a skill that scales much better, because the fact count does not grow with the diff size. A 340-line change and a 40-line change often rest on the same single assumption.
The second shift is asking the author, human or otherwise, to separate what was verified from what was assumed. If the pull request description has a section that says “this assumes X and Y; I confirmed X against the spec, I did not confirm Y,” you have restored the signal that fluency destroyed. This is not hard to ask for. It is hard to remember to ask for, and it is the kind of thing that only sticks if it is in a template rather than in a culture memo.
The third, and least popular, is to accept that some changes need a reviewer who did not read the description first. Anchoring is real. A description that explains the approach makes it much harder to notice that the approach is wrong, because you are now checking the code against the description rather than against the problem. For high-stakes changes, having someone form their own model of what the change should look like before reading the author’s account of it is expensive and genuinely effective.
The organisational version of this
There is a version of this failure that is not about individual reviews at all.
When every change arrives well-presented, review approval rates go up and review times go down, and both of those get measured and celebrated. Cycle time improves. The team looks like it is getting better at collaboration. There is no metric anywhere on the dashboard that says “our reviews are now less informative than they were,” because that is not a thing anyone instruments.
So the feedback loop points the wrong way. The team gets faster, the numbers get better, the defects appear weeks later attached to no particular pull request, and the retrospective conversation is about testing or about the incident, never about the review process that let it through, because the review process is showing excellent numbers.
I do not have a clean instrument to offer for this. The closest useful proxy I have seen is tracking where defects were introduced against how long the introducing change spent in review, and looking specifically at the fast-approved end. If your fastest reviews are producing a disproportionate share of your incidents, that is the calibration failure showing up in data. It is a lagging indicator by many weeks, which is unsatisfying, but it is better than nothing.
Where this breaks down
Several places, and one of them is serious enough that I would not want anyone to walk away from this article with the wrong conclusion.
Most confident pull requests are correct. That is the point of the technology. If you read this article and come away treating every well-presented change as suspect, you have imported the cost of scrutiny without the benefit, and you will be slower than you were before while feeling more virtuous about it. The base rate matters enormously. Confidently wrong is a failure mode worth naming precisely because it is rare enough to be surprising and consequential enough to hurt. Treating it as common is its own failure.
The old signal was never that good either. I have been slightly romantic about human authorship in this piece. In truth, plenty of senior engineers wrote fluent, confident, thoroughly tested, completely wrong pull requests, and their seniority made reviewers even less likely to look hard. Anyone who has watched a staff engineer’s change sail through on reputation knows the fluency proxy was already broken at the top of the distribution. Agents did not introduce this problem. They democratised it, which is a real change in magnitude but not a change in kind, and framing it as brand new leads teams to look for new solutions when older ones apply.
Verification has a cost curve that turns. “Separate verified from assumed” is good advice at moderate volume. At high volume it becomes its own bureaucracy, and there is a point at which the effort spent proving each change is safe exceeds the expected cost of the defects it prevents. Where that point sits depends entirely on your blast radius. A billing exporter and an internal admin dashboard deserve genuinely different answers, and a team that applies uniform rigour to both is wasting a large fraction of its review budget on changes that could safely be waved through.
And the fix can hollow out the thing it protects. If you push reviewers hard enough on rigour, some of them stop reviewing and start performing review: leaving a comment or two, ticking the boxes, approving. Rubber-stamping is often a response to unrealistic review expectations rather than a cause of them. Demanding deeper scrutiny of every change without reducing the number of changes is a reliable way to produce exactly the behaviour you were trying to prevent.
The takeaway
Code review always ran on a triage instinct fed by signals of effort and confidence, and agentic authoring flattened those signals to a constant. The change is not that pull requests became wrong more often. It is that the wrong ones stopped looking different from the right ones, so the reviewer’s attention no longer lands where it is needed.
The practical response is to review the claim rather than the code: find the one or two facts the change actually rests on, insist that authors separate what was verified from what was assumed, and reserve deep scrutiny for the parts of the system where being wrong is expensive. Not more review. Better-aimed review.
The next piece follows what happens when that misaimed attention meets work that quietly grows beyond its ticket: scope drift in agent-authored changes, and why it is so much harder to see than the human version was.