Skip to content

AI Code Review Has a Correctness Problem

Over the past two years, AI code review has gone from novelty to something many developers use every day, and the feedback can be genuinely impressive. Ask a modern model to review a pull request and you'll often get thoughtful comments about readability, naming, architecture, test coverage — the kind of observations that would have taken a human reviewer considerably longer to make.

I've spent the past while benchmarking several leading models against real pull requests, and the longer I looked at the results, the more one observation kept surfacing.

The reviews were often excellent.

The software wasn't.

Quality isn't correctness

My first explanation was that the models simply needed to get better, and that the next generation would close the gap. But the more I studied which problems the reviews caught and which they missed, the less the gap looked like a capability problem and the more it looked structural.

Software engineering has a rich vocabulary for how well code is built: readability, maintainability, testability, modularity, performance. Those qualities matter because they make software easier to understand, extend, and operate — and they are exactly what AI reviewers evaluate well. Correctness is different. Correctness asks a simpler and much harder question: does the software behave as intended under every supported condition? Clean architecture doesn't answer that. Descriptive names don't answer it. Even a comprehensive test suite only answers it for the conditions someone thought to test.

Correctness lives in behavior, and behavior is precisely what a review of the code's surface doesn't see.

The bugs that matter don't look like bad code

Consider the kinds of defects that actually reach production. A retry path that silently ignores the user's configuration. A regeneration workflow that behaves slightly differently from initial creation. Two unrelated entities that become merged because they happen to share a fallback identifier. A state transition that skips a validation every other path performs. An asynchronous execution path that quietly drops the tenant context every synchronous path preserves.

None of these necessarily produce ugly code. Most occur inside otherwise well-designed systems, and finding them requires tracing behavior across multiple files — following state transitions, comparing execution paths that are supposed to be equivalent, asking what happens after a partial failure. That is a fundamentally different activity from recognizing a code smell.

Language models are exceptional pattern recognizers. They notice duplicated logic, long methods, missing null checks, unclear abstractions — real contributions, all of them. But behavioral correctness demands something else: reconstructing the execution of a system. Does Create behave the same way as Update? Does the retry path preserve the same guarantees as the original request? Can state become inconsistent after a partial failure? Is identity propagated through every path, including the asynchronous ones? Questions like these can't be answered by evaluating a file in isolation. They require following evidence across an entire codebase, and that style of reasoning is substantially harder — for models and, frankly, for people.

Where does a verified bug belong?

The distinction stopped being philosophical when I tried to score reviews consistently. Review rubrics tend to break quality into dimensions — readability, maintainability, design, testing — and at first glance that looks comprehensive. Then a simple question surfaces: where does a verified behavioral defect belong? It isn't really a testing problem, or a design problem, or an interface problem, and I found that wherever it landed felt arbitrary. Inconsistent placement led directly to inconsistent scores.

The weighted average hides a worse failure mode. Imagine a pull request with excellent architecture, outstanding readability, comprehensive tests, and clean interfaces — and one verified bug that causes user configuration to be silently ignored. Every quality dimension scores high. The behavior is wrong. Depending on the weights, that pull request can still receive a respectable overall score despite containing a defect that should block deployment.

The problem isn't the arithmetic. The problem is asking a quality score to represent release risk. Engineering quality measures how well software is constructed. Release readiness measures whether it is safe to ship. The two overlap, but they are different measurements, and averaging them into one number quietly erases the only one that should be able to say no.

Confidence isn't evidence

One more lesson kept repeating during the evaluation. The models were remarkably confident, and confidence turned out to be only weakly correlated with whether a finding was real. A convincing explanation can be wrong. A hesitant observation can identify a critical production defect. So the useful question was never how confident is the model? It was what evidence supports this conclusion? Can the execution path be traced? Could an independent reviewer reach the same conclusion from the source?

Verification is what turns a plausible concern into engineering evidence.

Without it, confidence is just another prediction.

What this observation doesn't prove

I should be careful about how much weight this carries. Human reviewers miss exactly these bugs too — that is how they reached production with humans in the loop. Code review, human or artificial, was never the only correctness mechanism we have; tests, type systems, and staged rollouts carry most of that load, and an AI reviewer that only ever improved quality feedback would still be worth having.

I'm also not convinced this is a permanent limitation of the models so much as a limitation of how we deploy them. A reviewer handed a single diff cannot trace cross-file behavior no matter how capable it is. Systems that give the model the whole codebase, time to follow the execution, and a requirement to verify findings before reporting them may close much of the gap. That is why I suspect the next generation of AI code review will be defined less by larger models than by harnesses that treat correctness as the primary question rather than a side effect of quality.

Teaching the reviewer to notice

Finding another naming improvement is useful. Finding the one behavioral defect that would have reached production is a different problem entirely, and I've come to think it deserves to be treated as one — with its own kind of reasoning, its own evidence standard, and its own place in the score.

Software rarely fails because a variable name was unclear. It fails because, somewhere along the way, the behavior became wrong and nobody noticed. AI code review has learned, impressively, to evaluate how code is written.

The interesting work now is teaching it to notice.