Reviewing and Trusting AI-Written Code

You gave the tool a task, it churned for a minute, and now there is a green checkmark and a diff. The task looks done. The question that matters is whether it actually is, and answering that is now the main thing you do.

Writing the code was never the hard part once these tools got good. Deciding whether the code is right is. That is the skill this article is about.

Review is the real skill now

An AI coding tool stops when the model decides it is done. As covered in how AI coding tools actually work, that is not the same moment the code becomes correct. The loop ends on the model’s judgment, and the model optimizes for “this looks like a plausible solution,” not “this passes in production against my real requirement.”

So the finished diff is a claim, not a fact: the tool is claiming the task is done. Your job is to check the claim. Skip that step and you are not using an AI coding tool, you are shipping a stranger’s guess with your name on the commit.

The good news is you already have this skill. You review pull requests. The trick is applying the same rigor to a diff that arrived in thirty seconds instead of after a colleague spent a morning on it.

Treat every diff like a PR from a fast, junior engineer

The most useful mental model for an AI diff is a pull request from a teammate who is very fast, has read a lot of code, and has almost no judgment about your specific system. They produce something that compiles and reads well, and they will also confidently do the wrong thing without flagging it.

That framing sets the right defaults:

  • You read every line. You do not rubber-stamp because the author is usually good.
  • You are suspicious in exactly the places a junior slips: edge cases, error handling, security, and “did this even solve the actual problem.”
  • You do not assume the author tested it. You check.
flowchart LR
    DIFF([AI diff arrives]) --> REQ[Does it match<br/>the requirement?]
    REQ --> SEC[Security and<br/>secrets check]
    SEC --> API[Real APIs<br/>and imports?]
    API --> EDGE[Edge cases<br/>and errors?]
    EDGE --> RUN[Run tests<br/>and the code]
    RUN --> MERGE([Merge or<br/>send back])

The one difference from a human PR: there is no author to ask. You cannot leave a comment and get a reasoned reply about why they made a choice. You either verify it yourself or send the task back for another pass. That makes your read more important, not less.

What to check, specifically

“Review it carefully” is useless advice. Here is the concrete checklist, in the order that catches the most problems fastest.

1. Correctness against the requirement. Not “is this good code,” but “does this do the thing I asked.” Models are excellent at producing code that is internally consistent and solves a slightly different problem than the one you have. Read the requirement, then read the diff, and confirm they match. This is the single most common miss.

2. Security. Check the boundaries where untrusted input meets your system. String-built SQL instead of parameterized queries. User input rendered without escaping. A secret hardcoded because the model did not know your config setup. Auth checks quietly dropped from an endpoint. Models pattern-match on public code, and a lot of public code is insecure.

3. Hallucinated APIs and imports. Models invent functions, methods, and library features that sound right but do not exist. A call to requests.get_json() (there is no such method), a config flag that was never added, an import from a package that is not in your dependencies. These often pass a quick read because they look idiomatic. They fail the moment you run them, which is exactly why you run them.

Why does the model invent APIs that don't exist?

Because it is predicting plausible text, not looking anything up.

A model has seen millions of library.do_thing() patterns. When it needs a method to parse JSON from a response, .get_json() is a very plausible next token even if that method was never in the library. It has no runtime, no import resolver, no way to check; it produces the shape of correct code.

This is why a diff that reads perfectly can still fail on the first run. The fix is not “prompt better,” it is “run it.” A hallucinated import surfaces the moment real code executes.

4. Missing edge cases. Models write for the happy path. Look for the empty list, the null, the duplicate, the concurrent call, the timeout, the input at the boundary. Ask yourself what you would test if a junior wrote this, then check those cases exist in the code and the tests.

5. Tests. Did it write tests? Do they test behavior or do they just assert that the code does what the code does? A test that mocks everything and checks that a mock was called proves nothing. Read the assertions, not just the count.

Run the tests and the code, not just the diff

Reading a diff catches design and requirement problems. It does not catch runtime problems, and AI code has a specific class of runtime problem: it looks right and does not run. A hallucinated import, a wrong argument order, an off-by-one that only shows up on real data.

So reading is necessary and not sufficient. Run it.

  • Run the existing test suite. It should still pass. A green suite after the change is your baseline.
  • Run the new tests, and watch them actually exercise the change. Bonus: break the code on purpose and confirm the test goes red. A test that passes no matter what is worse than no test.
  • Run the actual code path, once, with real-ish input. Hit the endpoint. Call the function from a REPL. Trigger the flow in the app.
# Confirm the baseline still holds, then exercise the change itself.
pytest -q                        # whole suite still green?
pytest tests/test_login.py -q    # the new tests pass
# then hit the real path the change touched:
curl -s -X POST localhost:8000/login \
  -d '{"email":"a@b.com","password":"wrong"}' \
  -H 'content-type: application/json'

This takes two minutes and catches the entire hallucinated-API class of bugs, plus most requirement mismatches, because the code visibly does the wrong thing when you run it. Skipping it is where “the AI wrote a bug” stories come from. The AI wrote a guess; nobody ran it.

Building trust you can defend

Blanket distrust is as unproductive as blind trust. If you re-derive every line the tool writes, you have given up the speed that was the point. The goal is calibrated trust: knowing where these tools are reliable and where they routinely slip, and adjusting your review depth to match.

Where they tend to be reliable:

  • Boilerplate and glue: serializers, DTOs, straightforward CRUD, config wiring.
  • Mechanical transforms: renaming, reshaping data, converting formats.
  • Well-trodden patterns in popular frameworks, where the training data is dense.
  • Tests for code whose behavior is already clear.

Where they routinely slip:

  • Your domain logic and business rules, which are not in any training set.
  • Security-sensitive code, where “looks normal” and “is safe” diverge.
  • Concurrency, caching, and anything with subtle state.
  • Integrations with your internal systems and their quirks.
  • Anything where the requirement is ambiguous, because the model will resolve the ambiguity silently and confidently.

Calibrated trust means you skim the boilerplate and slow down hard on the domain logic and the security boundaries. It does not mean you merge anything unread. That is the one line that does not move. Even a one-line diff gets read, because one-line diffs are where a dropped auth check hides. The tool is fast, often right, and never accountable. You are the accountable one, and review is how you earn the right to trust it.

Common beginner mistakes

  • Reading for style, not correctness. The code is clean and idiomatic, so it looks done. Clean code can solve the wrong problem perfectly.
  • Trusting the green checkmark. The tool’s “done” is a claim about its own effort, not a verdict on your requirement.
  • Not running the code. Hallucinated APIs and wrong argument orders are invisible on a read and obvious on a run.
  • Accepting tests without reading them. A suite full of mocks asserting mocks is decoration, not coverage.
  • Same review depth everywhere. Skimming domain logic as fast as boilerplate. Spend your attention where the tool slips.
  • Merging unread because it is small. Small diffs hide the most damaging mistakes, like a removed permission check.

Questions you will face in production

“Isn’t reviewing every line slower than just writing it myself?” For a hard, novel problem, sometimes. For most work, no: reading well-shaped code and verifying it is faster than authoring it from a blank file, and the tool has already done the typing, the lookups, and a first draft of the tests. Review is where the time goes because review is where the value is now.

“How do I trust tests the AI wrote when the AI also wrote the code?” Don’t trust them on faith; make them earn it. Break the implementation on purpose and confirm the test fails. If it stays green, the test is asserting nothing. Also check that assertions test observable behavior and outputs, not that internal mocks were called.

“The diff is huge. How do I even start?” That is usually a scoping problem upstream, not a review problem. A diff too big to review is a diff too big to trust. Send it back and ask for smaller steps, or break the task up yourself. Handling large multi-file changes without losing the thread is its own skill, covered next.

What to remember

  • The tool stops when it thinks it is done, not when the code is correct; closing that gap is your job.
  • Treat every AI diff as a PR from a fast, junior engineer: read every line, trust nothing on faith.
  • Check requirement match first, then security, hallucinated APIs, edge cases, and real tests.
  • Run the suite and the actual code path; the read misses exactly what running catches.
  • Build calibrated trust: skim boilerplate, scrutinize domain logic and security, and never merge unread.

What to study next

Once you can review a small, clean diff with confidence, the next challenge is scale: changes that span many files, touch code you did not write, and are too large to hold in your head at once. That is where review discipline meets planning discipline, covered in large multi-file changes and refactors.

Further reading

Where this article comes from. This is a synthesis of common practice in AI engineering as of 2026, not a citation of any single paper. The sources above are where the mechanics come from. If you find an error or have a better source for a claim, the article gets fixed within a day, send me a note.


Auto-marks when you reach the end. Click to toggle.

If this helped, buy me a coffee

Everything is free. Tips keep me writing the rest.

Buy me a coffee →