~/engineering-practices/how-to-review-ai-generated-code.md

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

5 min readNovember 04, 2026by Gorav Singal#code-review#ai#best-practices
// TL;DR

Reviewing AI code is a different job, because AI code reads better than it works — it is clean, confident and plausible, so reviewers rubber-stamp it. 'Reads well' and 'is correct' are different things, and the model is optimised for the first one. So your job flips: the formatter already handles style, your real job is 'is this actually CORRECT, and does it solve the REAL problem?' Trust nothing, verify everything — you are the senior reviewing a very fast, very confident junior who sometimes makes things up. Look hardest where AI is characteristically weak: edge cases (it loves the happy path), error handling, security, hallucinated APIs (methods that don't exist), requirements fit (did it solve YOUR problem?), tests (do they test behaviour or just re-pass the buggy code?), concurrency, performance (N+1), and codebase fit (random new dependencies). The six-step flow: solve the real problem → run it with YOUR adversarial tests → read every line → hunt the AI smells → check the tests actually test → check it fits the codebase. The real risk is the scaling trap: AI writes 10x faster but your review capacity didn't, so slow down the merge, not the generation, and never approve code you couldn't have written and can't explain.

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

A few months back I approved a PR that was almost entirely AI-generated. It looked lovely — clean names, neat structure, tidy little helper functions, everything formated perfectly. I gave it a quick read, it all made sense, I clicked Approve. Two days later it broke in production on an empty input, something a five-line manual function would never have gotten wrong. That was my lesson: AI code reads better than it works, and reviewing it is genuinely a different skill. Let me share what I have learnt since.

If you want the general picture first, I wrote separately about code review in the AI era and the future of our job. This article is narrower and more hands-on: how do you actually review the code an AI wrote?

Why this is a different job

Why reviewing AI code is a different job. A human's pull request is usually smaller, written to a plan; the author can explain every line; bugs tend to look like bugs (messy, half-finished, obviously unsure); and you can ask why they did something and get a real answer. An AI's pull request is often large and very polished, with a confident tone and clean formatting, but with no human intent behind it, plausible-but-wrong silently, sometimes a hallucinated API call, and it passes the looks-right test easily. The trap and how your job changes: AI code reads so cleanly that we rubber-stamp it, but reads-well and is-correct are different things, so with AI code the formatter handles style and your real job is whether it is correct and solves the real problem.

When you review a human’s code, you are partly reading their intent. The code is usually small, written to some plan, and the author can tell you exactly why line 40 is there. Even the bugs are honest — half-finished, messy, obviously unsure.

AI code is the opposite. It arrives large, polished, confidently formatted, with lovely variable names — but there is no human intent behind it. The model pattern-matched something plausible. And “plausible” is not “correct”. The really dangrous part is that it reads so cleanly that your eye just slides over it and clicks Approve. The AI is, in a sense, optimised to pass your review rather than to be right.

So the job flips. With human code you spend a lot of effort on style, readability, small mistakes. With AI code the formatter and linter already handle style — that is not where you should spend your attention. Your real job becomes two blunt questions: is this actually correct, and does it solve the real problem? Trust nothing, verify everything. Treat it like the output of a very fast, very confident junior who occasionally just makes things up — because that is exactly what it is.

Where AI code breaks — look hardest here

AI is brilliant at producing clean, plausible-looking code and consistently weak at the same handful of spots. So aim your limited review time straight at them.

Where AI code breaks, nine hotspots to look hardest at. Edge cases: empty, null, huge, negative, unicode — AI loves the happy path. Error handling: swallowed errors, bare except, ignored failures. Security: injection, secrets, authz, unsafe eval. Hallucinated APIs: methods and params that don't exist — does it even run? Requirements fit: did it solve your problem or a nearby one? The tests: do they test behaviour or just pass the buggy code? Concurrency: races, shared state, locks — AI is weak here. Performance: N+1 queries, O(n squared), needless work. Codebase fit: conventions, existing utils, random new dependencies. Don't waste the review on style — the formatter and linter already handle that.

  • Edge cases. This is number one. AI writes beautiful happy-path code and quietly forgets empty lists, null, zero, negative numbers, huge inputs, unicode. Always ask: what are the inputs it did not handle?
  • Error handling. Look for swallowed errors — a bare except: pass, an ignored return value, a failure that is logged and then execution merrily continues as if nothing happened.
  • Security. AI happily reproduces insecure patterns from its training data: SQL built by string concatenation, secrets in the code, missing authorization checks, eval on user input. The Security Engineering course has the full list, but review AI code with security glasses on.
  • Hallucinated APIs. The model will confidently call a method or pass a parameter that simply does not exist, or that existed in some old version. First question, always: does this code even run?
  • Requirements fit. Did it solve your problem, or a slightly different nearby problem that it found easier? This is shockingly common. Read the actual task, then check the diff against it — not the other way round.
  • The tests. Be very careful here. AI often writes tests that just assert whatever the (possibly buggy) code currently does — so they pass, but they test nothing. And they usually cover only the happy path. A green tick from AI-written tests means very little by itself.
  • Concurrency, performance, codebase fit. Races and shared state (AI is weak at reasoning about these), N+1 queries and accidental O(n²), and the small stuff like ignoring your existing utility functions or pulling in a random new dependency for something trivial.

A six-step review flow

Here is the simple flow I follow now. In order, because the order matters.

A six-step review flow for AI-generated code. Step 1: does it solve the real problem — read the task, not just the diff. Step 2: run it plus your own adversarial tests, not its happy-path tests. Step 3: read every line — if you can't explain a line you can't approve it. Step 4: hunt the AI smells — edge cases, error handling, security, hallucinated APIs. Step 5: do the tests actually test behaviour, not just re-pass the buggy code. Step 6: does it fit the codebase — conventions, existing utils, no random dependencies. The real risk is the scaling trap: AI writes code ten times faster but your review capacity did not, so slow down the merge not the generation, and nobody approves AI code they could not have written and cannot explain.

  1. Does it solve the real problem? Read the task first, then the diff. Don’t let a beautiful diff distract you from the fact that it answered the wrong question.
  2. Run it — with YOUR tests. Not the AI’s happy-path tests. Write a few adversarial inputs yourself — the empty case, the huge case, the malformed case — and watch it fall over. This single step catches most of it.
  3. Read every line. The old rule, more important than ever: if you cannot explain why a line is there, you do not get to approve it. No skimming.
  4. Hunt the AI smells — the nine hotspots above. Go looking for them specifically; don’t wait to stumble on them.
  5. Do the tests actually test anything? Change the code to be deliberately wrong and see if a test fails. If nothing goes red, the tests are decoration.
  6. Does it fit the codebase? Conventions, existing helpers, no needless new dependencies, no reinventing something you already have.

One small trick that helps a lot: ask the AI to review its own code. “What edge cases does this miss? What could go wrong in production?” It is often surprisingly good at poking holes in its own work — but of course, you still verify what it says, because it can be confidently wrong about that too.

The real danger nobody prepared us for: the scaling trap

Here is the thing that actually worries me, more than any single bug. AI lets us generate code maybe ten times faster. But our ability to review code did not suddenly become ten times faster — a human still has to read and understand it, at human speed. If you let generation outrun review, you are not going faster; you are just shipping unreviewed code with extra steps, and the bugs pile up quicker than before.

So the fix is a bit counter-intuitive: slow down the merge, not the generation. Keep PRs small so they can actually be reviewed properly. Protect real review time. And agree on one simple team rule — nobody approves AI code they could not have written themselves and cannot explain. Because the moment you approve it, it is yours. You own it, at 2am, when it breaks. Review it like you will have to.

Takeaways

  • AI code reads better than it works. Clean and confident is not the same as correct — don’t let a polished diff earn a rubber stamp.
  • The job flips: the formatter handles style, so spend your review on “is it correct, and does it solve the real problem?” Trust nothing, verify everything.
  • Look hardest at the AI weak spots: edge cases, error handling, security, hallucinated APIs, requirements fit, the tests, concurrency, performance, codebase fit.
  • Follow the flow: real problem → run with YOUR tests → read every line → hunt the smells → check the tests test → check it fits.
  • Mind the scaling trap: slow down the merge, not the generation. Never approve AI code you couldn’t have written and can’t explain — you own it either way.

More in this series: code review in the AI era, commits and pull requests, the big-picture future of software engineering, and the playbook to stay valuable. To sharpen the verification eye itself, the Security Engineering and System Design Masterclass courses are a good investment.

Share

Related Posts

How to Debug AI-Generated Code: Understand First, Fix Last

How to Debug AI-Generated Code: Understand First, Fix Last

In the last article I wrote about reviewing AI code before it goes in. This one…

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

In the previous article I argued that AI is not killing software engineering…

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

Every few weeks somebody sends me a scary headline — “AI will replace all…

Pull Request Best Practices in the AI Era

Pull Request Best Practices in the AI Era

A pull request is where individual work becomes team work — and where most of…

Code Review Best Practices in the AI Era

Code Review Best Practices in the AI Era

Code review is the last line of defence before code becomes everyone’s problem…

Git Commit Best Practices in the AI Era

Git Commit Best Practices in the AI Era

A commit is the smallest unit of a project’s history, and it is written far more…

Latest Posts

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

In the previous article I argued that AI is not killing software engineering…

How to Debug AI-Generated Code: Understand First, Fix Last

How to Debug AI-Generated Code: Understand First, Fix Last

In the last article I wrote about reviewing AI code before it goes in. This one…

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

Every few weeks somebody sends me a scary headline — “AI will replace all…

The Transactional Outbox Pattern: Never Lose an Event Again

The Transactional Outbox Pattern: Never Lose an Event Again

This is one of those patterns that looks like nothing until you get burnt once…

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Here is a question I like to ask in interviews: your bank account shows a…

CQRS Pattern, Explained Simply: Split Your Writes From Your Reads

CQRS Pattern, Explained Simply: Split Your Writes From Your Reads

Most of us start our career with one model for everything. The same class, the…