Walkthrough

Reviewing generated code is a different read, not a harder one.

Generated code is fluent in a way human code often is not, and fluency is what defeats review. The method below replaces reading with a sequence of specific questions, asked in a fixed order.

Before you read a line

Get three things: what the change is meant to do, what it replaces, and the automated report. Without the first two you will review the code against your own assumption of its purpose, which is the most common review failure.

Run the checks first, or read the report if somebody already did. Secrets, unsafe execution, empty catch blocks and syntax problems are settled questions, and spending your attention on them is a waste of the only expensive resource in the process.

Then decide how much attention this deserves: a routine landing page change and a session handler do not deserve the same hour.

Read the diff for intent, not style

Start with the shape of the change. What files, what boundaries, what new dependencies, what was deleted. A generated change that quietly rewrites three unrelated modules is a change you should send back before reviewing further.

Then read the entry point. Not the helper functions, not the utilities, but where the request or the click arrives. That is where the change either makes sense or does not.

Style is the last concern and mostly the linter's job. Reviews that open with naming conventions use up their attention before reaching the part that matters.

Hunt the four signature defects

Generated code fails in repeatable ways, and four of them account for most production incidents.

  • A credential in source: an API key, a connection string, a private key block, a literal assigned to something called token or secret.
  • Unsafe execution: eval, the Function constructor, HTML assigned to innerHTML, SQL built by concatenation, input reaching a shell.
  • Swallowed failure: an empty catch block, a default returned instead of an error, a log that goes nowhere.
  • An invented API: a method, an option name or an event that reads plausibly and does not exist on the pinned version.

Verify the dependencies exist

The invented API is the expensive defect because it passes review and fails at runtime, often in a version nobody tested. Check the import resolves, the method exists, and the options match the documentation rather than a plausible variant.

Where the code relies on a library behaviour, ask whether that behaviour is documented or assumed. Assumed behaviour is what changes between minor versions, and generated code assumes it constantly.

If the change adds a dependency, read why. Generated code reaches for a package to solve what three lines would have solved, and every package is a future upgrade.

Trace one failure path end to end

Pick the most likely failure: the external call timing out, the response arriving empty, the user submitting twice. Follow the code from that event to what the user sees.

In generated code the path usually ends in silence: a caught error, a fallback value, a spinner that never resolves. Silence in an error path is a bug with a delay fuse, and it becomes an incident when a customer notices before you do.

Then ask whether the failure is observable. Somewhere a log, a metric or an alert should record it; if nothing does, the code can fail indefinitely without anyone knowing.

Look for the test that was not written

Generated changes usually arrive without tests, because the prompt described a feature rather than a behaviour. The absence is the finding, not an oversight to note politely.

Ask for the cases the generation skipped: empty input, a non-200 response, an unauthorised caller, a duplicate submission, a very large value. Those are exactly the paths the happy-path implementation does not handle.

For anything touching money, permissions or deletion, insist on an integration test against the real dependency. A unit test with a mock will happily confirm an invented API.

Check the boundaries

Access control is where generated code is most confident and most wrong. Look for the authorisation check on the handler, not on the route decorator, and confirm it fails closed when the check itself errors.

Money needs idempotency: a retried request must not charge twice, and a partial failure must not leave an order half-created. Deletion needs a confirmation path and a recovery window.

If the change touches none of these, say so and move on. Most changes do not, and inventing risk is as much a review failure as missing it.

Write a review that saves the author time

Separate blocking findings from suggestions, explicitly. A comment wall where everything reads with the same weight forces the author to guess what you actually require.

For each blocking finding, name the location, the consequence and the fix. Not this is unsafe but this value reaches innerHTML at line 42, so an attacker-controlled name can inject script; use textContent.

If the change is good, say what you checked and what you did not. That single sentence tells the author where the remaining risk sits.

Scope your sign-off

Approval means you read it. It does not mean the change is correct, and the difference matters the first time something goes wrong six months later.

Write the scope in the approval: reviewed for access control and error handling in the session route, for example. That sentence is auditable; approved is not.

Keep the automated report with the pull request so the next reviewer inherits the findings and the reasoning rather than repeating both.

When the change is too large to review

Large generated diffs are the normal case, and reviewing six hundred lines properly is not possible in the time a review usually gets.

Ask for the change to be split by boundary, or for a short design note first: what it touches, what it assumes, what it deliberately does not handle. A note that cannot be written is a signal the change is not understood by its author either.

It is legitimate to review in slices, and it is legitimate to block on a change being unreviewable. Silently approving volume is how a codebase acquires defects nobody has ever read.

What this covers

  • Purpose first

    What the change does, what it replaces and the automated report in hand.

  • Diff shape

    Files, boundaries and new dependencies read before any function body.

  • Signature defects

    Secrets, unsafe execution, swallowed errors, invented APIs.

  • One failure traced

    A realistic failure followed from event to what the user sees.

  • Scoped approval

    What you reviewed written into the approval, not just a tick.

Questions this page usually raises

How long should reviewing generated code take?
The mechanical pass is seconds with a checker. The read that matters is small: intent, one failure path, dependencies and boundaries. Reviewing every line is rarely possible and rarely the best use of an hour.
Is generated code less safe than human code?
It fails differently. It is more likely to have credentials in examples, swallowed errors and invented APIs, and less likely to have tests. The review should compensate for those specific tendencies.
Should I require tests with every generated change?
Require them where behaviour matters, which is anything outside presentation. For presentational changes a visual check is the more honest test.
What if I cannot verify the API exists?
Check the installed version in the lockfile and the documentation for that version. If neither settles it, treat it as unverified and say so in the review rather than approving on plausibility.
Does the review need to send the code anywhere?
The checks run in the browser against pasted source, so proprietary code does not leave your machine during the automated pass.

Review AI code

Run the checks, then decide who has to sign it.

The automated pass is free, needs no account and uploads nothing. Escalate to a named reviewer only where being wrong is expensive.