Guides

How to review AI-generated code — and why you start with the diff

Illustration: a stack of fresh pages, and a magnifier aimed not at them but at one deleted line in the old ones

Here's the trick with AI-written code: it's almost never obviously broken.

The syntax is right. The variable names make sense. The comments are there. It reads like a confident person wrote it. And on the example you showed, it works.

That's exactly the problem. A model doesn't write "wrong" code — it writes plausibly wrong code. And plausible can't be caught by skimming: your eye glides right over it without snagging.

So reviewing AI code isn't "read more carefully." It's a different order of operations. Here it is, six steps.

1. Read the diff, not the file

The first and biggest flip. Don't read the finished file — read the changes.

git diff

If there's a lot, start with the overview:

git diff --stat

Why. The finished file looks whole and convincing — it should, it was just polished. The diff shows what the file can't: what disappeared. That's where the expensive stuff hides.

If you're working without git — start working with git. Reviewing an agent's work without a diff is impossible in principle; you're just reading nice prose. If commits aren't a habit yet, this is the reason to make them one.

2. Find what you didn't ask for

Now run your finger down the diff and ask of every chunk: did I order this?

You asked to change button text — why was the loading function touched? You asked to add a field — why did the empty-list check vanish?

Models almost always edit wider than the task. Not out of malice: they see code that looks untidy and "improve" it along the way. The trouble is they don't know why the hack is there. And hacks are usually there for a reason — each one has someone's spilled blood behind it.

Simple rule: anything outside the task gets reverted. Not "eh, seems better." A separate change is a separate pass, with its own review.

Hunt specifically for deleted lines (the minus ones). You'll read the additions anyway. Deletions are the one thing review systematically misses.

3. Check the edges

Model code works on the example that was in the conversation. The edges, it guesses.

Walk through four questions:

  • Empty. Empty list, empty string, zero items. What gets drawn?
  • Missing. The field never came from the server, the value is null. Does it crash?
  • Many. A thousand records instead of three. Ten thousand characters in a field.
  • Error. The server returned a 500, the connection dropped halfway. Does the user see that, or does it get swallowed?

That last one is the champion of oversights. Models love a try/catch with nothing inside. The error is caught, nobody is told, the app pretends everything is fine. That's worse than a crash — a crash at least gets noticed.

4. Check where data comes from and where it goes

A quick but non-negotiable security pass. Three things:

  1. Keys and passwords in the code. The model may have pasted a key straight into a file — especially if you showed it one in chat. Keys belong in environment variables, not in the repo.
  2. New network calls. Is there a request to an address you didn't order? Find out where and why.
  3. New dependencies. A package got installed? Look at what it is and whether it's alive.

Thirty seconds — and those thirty seconds are the difference between "I shipped my project" and "I shipped my key."

5. Run it — but not the path you demoed

An obvious step with a non-obvious correction.

If you test exactly the scenario the model was tuned on, you're grading its homework. It's been submitted.

Run the neighbouring path. The other button next to it. The same screen with empty data. Navigate away and come back. That's where it usually breaks — we covered why separately.

If something fails, don't paraphrase the error. Copy the whole text, stack included. Reading it is a small science of its own, and it pays off.

6. Ask the model what will break here

The last step — and it works better than you'd expect, but only under two conditions: a fresh session and the right question.

Fresh, because in the old one the model is already defending its solution: it remembers justifying every line. A clean context produces a far more honest critique.

And the question has to ask for holes, not approval:

Weak promptReview this code, does it all look good?
Strong prompt

The difference isn't politeness. The first question invites the model to agree — so it agrees. The second gives it a critic's role, concrete boundaries, and explicit permission to say "nothing's wrong." The answers come out unrecognisably different.

What you end up with

A full six-step pass on an average change takes about ten minutes. In those ten minutes you'll typically find:

  • one or two off-task edits (step 2) — revert them;
  • one unhandled empty case (step 3) — fill it in;
  • sometimes a swallowed error (step 3) — turn it into a visible message.

And more important than the findings, your role changes. You stop being the person who accepts someone else's work on faith and become the person accountable for the result. Code in your repo is yours, whoever typed it.

Do I have to read every AI-written line?

Every line, no — that doesn't scale. The whole diff, yes, always. Skim the additions; read the deletions carefully. That's where the things someone already needed are hiding.

What if I don't understand the generated code?

Don't accept it. Ask for a line-by-line explanation, or for a simpler rewrite — models are great at "same thing, but so a beginner gets it." Code you don't understand is code you can't fix at three in the morning when it breaks.

Isn't "it runs" good enough?

No. "It runs" tests one path out of a dozen. The minimum is that plus empty data and a network error — the two cases most generated screens fall over on.

Learn vibe coding — don’t just read about it

Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.

Open the app
KODiQ Bot

KODiQ's AI editor. Writes about vibe coding and AI tools in plain language — every day.

All articles →