Reviewing AI-generated code: what to keep and what to delete

By on

A person carving a piece of wood by hand

Agents write working code quickly and generously. The generosity is the problem. Most of what comes back is speculative, and the skill that matters now is deciding which parts were load-bearing.

Generating a working feature in an afternoon stopped being remarkable. Most teams can do it. The interesting question moved downstream, to what you do with the output.

Agents are good and they are relentlessly generous. Ask for a form and you get validation nobody requested, a configuration layer that will never be configured, and error handling for states that cannot occur given the call sites.

Why the extra code is not free

Every speculative abstraction is something the next person has to read before they can change anything. It is another branch that can rot when a dependency moves. It is one more place a bug can sit unnoticed, because nobody exercises the path.

The cost does not show up in the pull request. It shows up nine months later when someone needs to change the thing and has to first work out which of the four code paths is live.

The four things I delete every time

  • Options with one caller. A parameter that is always passed the same value is not flexibility, it is a branch pretending to be a feature.
  • Error handling for impossible states. If the type system or the call site rules out null, catching null adds a lie to the code.
  • Wrapper layers with no second implementation. An interface with exactly one implementer is indirection, and it will still be there when the second implementer never arrives.
  • Comments restating the line below. Generated code narrates itself constantly and it ages worse than the code does.

The one thing I almost always keep

Tests. Agents write more of them than most humans will, and coverage of the boring cases is genuinely useful. The caveat is that generated tests tend to assert what the code does rather than what it should do, so they pass by construction and catch nothing.

The cheap check is to break the implementation on purpose and see whether the test fails. If it does not, the test is describing the bug, not preventing it.

Reviewing it like what it is

The mental model that works is a fast, confident, slightly overeager junior. Not because the code is bad, but because the failure mode is the same. Reasonable-looking work, produced faster than it can be considered, with an instinct to add rather than remove.

You would not merge that unread from a person. The volume is the only thing that changed.

Where to start

  • On your next generated pull request, delete every parameter with a single call site before reviewing anything else. It usually removes a third of the diff.
  • Pick one generated test and break the code it covers. If it still passes, you have learned something about all of them.
  • Add a line to your review checklist: what in this diff exists because it was asked for, and what exists because the model likes symmetry.
  • Track diff size over a month. If it is growing while feature count is flat, the generosity is winning.

Tags: ai, engineering, craft