Engineering

Why AI-generated code fails code review in a different way

Editorial · Reveneau · September 9, 2026

Why AI-generated code fails code review in a different way

We have watched a lot of pull requests go through review in the past two years, and the ones that cause trouble later almost never look troublesome at the time. They are tidy. They are consistently named. They have docstrings. They get approved in four minutes with a thumbs up.

That is the problem, and it is worth being precise about why.

Code review rests on an assumption nobody writes down: that careless work looks careless. For most of the history of the practice this was true. A function doing four things, a variable called data2, a comment arguing with the code beneath it, three styles in one file because two people disagreed. These are signals. A good reviewer follows them to the defect, and the reason experienced reviewers are fast is that they recognise these warning signs rather than reading every line.

Generated code shows none of those signals. It applies the same clean style to the part that was carefully specified and the part that was invented from a plausible guess. Consistency stops being evidence of care.

What actually gets past review

Which defects appear, and how often, is the useful part, and it is measurable.

Veracode's Spring 2026 research tested more than 150 models across 80 coding tasks in four languages. Generated code compiled and ran correctly over 95 percent of the time. It passed security checks in roughly 55 percent. The average matters less than the difference between types of vulnerability. SQL injection passed at 82 percent, insecure cryptography at 86 percent, cross-site scripting at 15 percent, and log injection at 13 percent.

Look at which classes did well. SQL injection is the one every developer has been trained on, the one with an obvious pattern, the one a reviewer spots because the string concatenation is right there. Cross-site scripting and log injection are the opposite: the defect is spread across a few lines that each look completely ordinary, and identifying it requires tracking where a value came from and where it is eventually used. That is exactly the analysis a human reviewer does worst, especially as the tenth review of the afternoon.

So the failures are concentrated in the types of defect review was already weakest at, and generation produces more of them.

The second measurable thing is code that is almost right. Stack Overflow's 2025 survey found the single biggest developer frustration with AI tools was output that is "almost right, but not quite", named by 66 percent, with 45.2 percent reporting that debugging generated code takes longer than writing it themselves. Obviously wrong code costs nothing: it is deleted in seconds. Almost-right code is read three times, discussed, approved, and then turns out to be the cause of an incident five weeks later.

The three assumptions that stopped being true

That the author understood the change. Review has always been a conversation with someone who made decisions and can explain them. When a model produced the diff, nobody knows the reasoning. The reviewer ends up working out the requirement backwards from the implementation, which is the opposite of what review is for.

That effort limits volume. Writing code used to be slow enough to limit how much arrived for review. That limit is gone on the production side and unchanged on the review side. Attention cannot grow with volume: a reviewer facing four hundred lines reads the last hundred materially worse than the first, and everyone knows this and approves anyway.

That style shows quality. Discussed above, and it is the one that quietly removes an experienced reviewer's best tool.

What to do about it

Four changes, in the order they give results.

Move everything mechanical to a machine

A human should never be checking formatting, obvious null handling, or whether an error is silently ignored. Those are machine jobs and a machine does them perfectly at any volume. Every minute a reviewer spends acting as a linter is a minute not spent on the question of whether the change should exist.

This is the cheapest change and most teams have already done half of it.

Verify against the specification, not the implementation

The mistake is generating tests from the code. A suite written by reading the implementation asserts that the code does what it does, passes automatically, and proves nothing. It is worse than no suite, because it passes.

Checks have to come from what the software must guarantee, written before or independently of the code. The method is in writing specs an agent can verify, and the failure modes are catalogued in when evals give false confidence. The direct test: break something deliberately and confirm the suite notices. If it does not, the passing checks mean nothing, and it is much better to learn that today.

Make the change state its own intent

If an agent opens the pull request, the description has to supply what the absent author would have: what was asked for, what approach was chosen, what was deliberately not done, and what the reviewer should look at most carefully. A diff with no stated intent is not reviewable, only readable.

We treat a missing rationale as a review blocker in the same way a failing test is. It sounds bureaucratic and it takes about ninety seconds.

Limit the size automatically

Reject changes that are too large for a reviewer to understand all at once. Not as a guideline: as a rule that a bot enforces.

This is unpopular and it is the most effective thing on the list, because every other improvement is defeated by a two thousand line diff. If the work genuinely cannot be split, that is worth knowing before it is written, not after.

What the reviewer is for now

Narrow the human job deliberately, and it gets better rather than worse.

Should this change exist at all. Does it match what was actually asked for. How much breaks when it fails. What did somebody decide here that no test could express. What is missing that nobody would notice was missing.

That last question is the one only a person can ask, and it is the whole argument for keeping humans involved. A check verifies a rule someone thought of. It cannot notice a rule nobody thought of. Related reading is in what good code review looks like when nobody wrote the code and how to review AI-generated code.

The uncomfortable part

Some of this is a volume problem with no clever solution. If a team produces three times the change it did last year and has the same number of reviewers, something has to be reduced. Usually it is how carefully each change is read, and nobody says so, while the approval rate stays reassuringly high.

The honest options are to slow production, add review capacity, or build verification good enough that a reviewer can accept work without reading every line. The third is the only one that keeps working as volume grows, and it is engineering work that has to happen before the volume arrives rather than after.

Thanks to the reviewers who told us which approvals they regretted and why. In every case the code had looked fine, and looking fine had stopped telling them anything.

Review used to ask whether the code was written carefully. Now it has to ask whether it is correct, and those were never the same question.

Related guide: Enterprise AI coding governance.

Sources

  • Veracode, Spring 2026 GenAI Code Security update: 150+ models, 80 tasks across four languages; over 95 percent syntactic correctness against roughly 55 percent security pass rate, with SQL injection at 82 percent, cross-site scripting at 15 percent, and log injection at 13 percent.
  • Stack Overflow 2025 Developer Survey, AI section: 66 percent name "almost right, but not quite" as their biggest frustration; 45.2 percent report debugging generated code takes longer than writing it.

Common questions

Why does code review work less well on AI-generated code?

Because review evolved to detect the signals of careless human work, and generated code does not show them. Inconsistent style, confused naming, and half-finished structure are the cues a reviewer follows to a defect, and generated output is uniformly tidy across both its correct and its wrong parts.

Which types of defect get past review most often in generated code?

The ones that cannot be seen at normal reading speed because no single line looks wrong. Veracode's Spring 2026 testing found cross-site scripting passing security checks in only 15 percent of generations and log injection in 13 percent, while SQL injection passed at 82 percent, so the risk is concentrated in types of defect that reviewers read past quickly.

What does "almost right, but not quite" mean in practice?

Code that compiles, runs, passes the obvious cases, and is wrong in a condition nobody tested. Stack Overflow's 2025 survey found 66 percent of developers naming this as their biggest frustration with AI tools, and it is expensive precisely because it gets past the cheap checks.

Should reviewers read every line of generated code?

No, because attention cannot grow with the amount of code and a reviewer who must read four hundred lines reads the last hundred worse than the first. The workable split is that machines check what machines can check, and the human spends their attention on whether the change should exist and what happens when it fails.

What should a human reviewer actually look at?

Intent and consequences: does this match what was specified, is this the right approach, how much breaks when it fails, and what did the author decide that a test cannot express. Those questions are where human judgment beats any automated check, and they are what gets pushed aside when the reviewer is also acting as a linter.

How do you stop a review queue from becoming the slowest step?

Reduce what needs human attention rather than asking humans to go faster. That means automated checks that encode the guarantees, small changes rather than large ones, and an explicit rule that a change nobody can review at the current size gets split.

Does requiring more tests solve the problem on its own?

Not if the tests were generated from the same misunderstanding as the code, which is the common failure. A suite written from the implementation asserts that the code does what it does, so it passes automatically and tells the reviewer nothing.

How do you tell whether your existing checks can actually fail?

Break something on purpose and confirm the suite notices, then repeat it in the two or three areas where a failure would matter most. Inherited and generated suites frequently contain assertions that would pass against an empty implementation, and because every check passes nobody has ever questioned them, so the discovery only happens when an incident proves the checks never tested anything.

Is it reasonable to reject a change simply for being too large?

Yes, and it is one of the few review rules worth enforcing mechanically. Review quality falls as size grows, so a change large enough that no reviewer can understand it all at once has effectively skipped review no matter who approved it.

What changes about review when an agent opens the pull request?

The author is no longer available to explain intent, so the change has to include it: what was asked for, what was decided, what was deliberately left out. Without that, the reviewer is working out the requirement backwards from the diff, which is exactly the work review is meant to avoid.

Does this mean AI-generated code is worse than human code?

It means it is wrong in different places, which matters more than better or worse. It is more likely to be syntactically clean and conventionally structured, and more likely to carry a security defect or a defect in an unusual case that looks unremarkable, so a process designed for human error rates misses it.

Start a project