All posts
6 min read

How to Review a Pull Request Written by an AI Agent

AI-written pull requests are tidy, confident, and plausible — which makes them harder to review, not easier. The failure modes that differ from human code, the order to read a PR in, and a checklist that catches what skimming misses.

AI agentsdeveloper experienceengineering management

A pull request from an AI agent looks like a good pull request. Consistent formatting, sensible names, a clear description, often tests. That polish is the problem: the signals reviewers have learned to rely on — messy code means careless work, clean code means careful work — stop meaning anything.

AI-written code fails differently from human-written code. It doesn't get lazy near the end or leave debug statements behind. It confidently solves a slightly different problem, invents an API that doesn't exist, or makes a test pass by changing the test. Reviewing it well means looking for those failures specifically.

How AI-written code goes wrong

It solves the problem it understood, not the one you meant. The code is internally consistent and correct — for a slightly different requirement. This is the most common failure and the hardest to spot, because nothing in the diff looks wrong.

It invents things. A method that doesn't exist on that library, a config option that was never supported, a package name that sounds right. Usually caught by the build — unless the invented thing is in a code path the tests don't reach.

It makes the tests pass rather than the code work. A test that asserted the right behaviour gets its assertion changed. A failing test gets skipped. A mock is adjusted until the test is green and meaningless.

It over-builds. Abstractions for flexibility nobody asked for, configuration options for one use case, a helper module for code used once. Every line is reasonable; the total is too much.

It duplicates instead of reusing. A new date-formatting function next to the three you already have, because it didn't look.

It handles errors by hiding them. A try/catch that swallows the exception and returns an empty list. The app stops crashing; the bug becomes invisible.

It drifts out of scope. "While I was in there, I also refactored…" — changes you didn't ask for, mixed into the ones you did.

It's confident about everything. The PR description says the feature is complete and tested, whether or not that's true.

Read it in this order

Don't start at the top of the diff. Start where the risk is.

1. The description, against the original request

Before any code: does the summary describe what you actually asked for? If the agent restates the task slightly differently, you've found the problem already.

2. The tests

Read the tests before the implementation. They tell you what the agent thinks the requirement is. Then check:

  • Do they test behaviour, or just that functions were called?
  • Were any existing tests changed? Every change to an existing test's expected value needs a reason. This is where "make the tests pass" hides.
  • Were any tests skipped, deleted, or marked as expected failures?
  • Is there a test for the edge case you actually care about?

3. The dangerous files

Look for changes to:

  • Database migrations — anything that drops, renames, or changes types. Is it reversible? What happens to existing data? (What Are Database Migrations?)
  • Authentication and authorisation — who can do what. Especially: is a permission check enforced on the server, or just hidden in the interface?
  • Dependencies — any new package in package.json or equivalent. Does it exist, is it maintained, is it actually needed?
  • Configuration, CI, and environment files — changes here affect everything.
  • Anything touching payments or secrets.

4. The implementation

Now read the code, looking for the failure modes above: invented APIs, swallowed errors, duplicated helpers, over-built abstractions, and changes outside the scope of the task.

5. The running result

For anything user-facing, run it. Click through the feature. Try the obvious wrong inputs. A preview environment per branch makes this a thirty-second job rather than a chore — see Preview Environments for Every Branch.

The checklist

  • The PR does what was asked — not a nearby variant of it
  • Tests test behaviour, including the edge cases that matter
  • No existing test's expectations changed without a stated reason
  • No tests skipped or deleted
  • Migrations are reversible and safe for existing data
  • Permission checks happen on the server
  • Every new dependency exists, is maintained, and is needed
  • No errors silently swallowed
  • No duplicated helpers where existing ones would do
  • No unrequested changes mixed in
  • No secrets, credentials, or debug endpoints added
  • It works when you actually run it

Make the agent do the first pass

You can take a lot of this off your plate by asking the agent to review its own work adversarially before you see it — with specific questions, not "does this look good?":

List every existing test you modified and why each change was correct. What did you assume about the requirement that I didn't state? Which code paths are not covered by tests? What did you add that I didn't explicitly ask for?

A separate reviewer agent with read-only tools and your checklist in its instructions is even better: it didn't write the code, so it isn't invested in it. Claude Code Subagents and Claude Code Skills show how to set one up.

Then verify its answers. An agent reviewing itself will occasionally claim a test exists that doesn't.

Make PRs reviewable in the first place

The best review happens before the PR exists:

  • Keep them small. One PR, one purpose. Ask the agent to split large changes. A 2,000-line PR doesn't get reviewed; it gets approved.
  • Agree the plan first. If the direction was checked in plan mode, review is about execution rather than discovering the approach was wrong.
  • Put your standards in writing. Conventions the agent should follow belong in CLAUDE.md, so they're applied before review rather than enforced during it.
  • Automate the mechanical parts. Formatting, linting, type-checking, and tests should run in CI and in hooks. Human attention is for judgement.
  • Require a green CI run before review. Never review code that hasn't been built and tested.

The mindset

Review an AI's pull request the way you'd review a very fast, very confident new colleague who has never seen your codebase before and never says "I'm not sure." The code will usually be fine. Your job is to find the places where it isn't — and those places are predictable.


EasySpawn gives each branch its own live URL and runs Claude Code against the real app and database — so an AI-written PR arrives already built, tested, and clickable. See how it works or join the waitlist.

Related: Running Claude Code Agents in Parallel With Git Worktrees · A Security Checklist for Vibe-Coded Apps · How to Test Your App Before Launch · Set Up CI With GitHub Actions · How to Read a Diff · Refactoring AI-Generated Code

Keep reading