← All writing

Code Review in the Age of AI-Generated Pull Requests

When most of the diff was written by an AI tool, traditional review habits break down. The volume goes up, the author's understanding goes down, and the reviewer needs a different checklist.


A colleague sent me a pull request last month. Fourteen files changed, clean commit messages, tests passing, well-formatted code. I started reviewing and something felt off. The changes were technically correct but lacked a coherent thread. One file used a guard clause pattern, another used early returns, a third used nested conditionals for the same kind of check. The naming was inconsistent across files but consistent within each file — like three different developers had each written one piece.

When I asked about it, the answer was honest: “I used Claude Code for most of it.”

That is not a problem. The problem is that the review process has not caught up.

The volume problem

AI tools write code fast. That is their primary value proposition and their primary review risk.

A developer working manually might produce a pull request with three changed files and a hundred lines of diff. The same developer with an agentic tool might produce fourteen files and five hundred lines. The behavior might be identical. The review burden is not.

Most code review works on an implicit assumption: the author understood every line they wrote. When a human writes code manually, that assumption is roughly true. They chose the variable name, they decided the control flow, they know why this condition comes before that one. When a tool writes the code, the author may have verified the behavior without examining every implementation choice.

That shifts the reviewer’s job. You are no longer just checking for mistakes the author might have missed. You are checking for mistakes neither the author nor the tool noticed — because neither one had the full picture the reviewer needs to reconstruct.

What to look for changes

Traditional code review has a natural priority order: correctness, security, performance, readability, style. That order still works, but the distribution of problems shifts with AI-generated code.

Correctness is usually fine at the surface level. AI tools are good at producing code that compiles, passes the obvious tests, and handles the happy path. The bugs hide in edge cases, boundary conditions, and interactions between the changed code and the rest of the system.

Questions to ask:
- What happens when this input is null, empty, or unexpectedly large?
- Does this change interact with any state that the test does not cover?
- Is this solving the stated problem, or a slightly different problem that looks similar?

Consistency becomes a bigger concern. A human developer builds habits within a codebase — naming patterns, error handling approaches, abstraction preferences. An AI tool does not carry those habits between prompts. It produces locally correct code that may not match the surrounding conventions.

Look for:

  • Different error handling patterns in the same module
  • Naming that does not match adjacent code
  • New abstractions that duplicate existing ones
  • Import styles or file organisation that drift from the project norm

Unnecessary complexity appears more often. AI tools tend to be thorough. When asked to handle an error, they might add a retry mechanism, a fallback, and a logging layer where a simple exception would have been enough. Each addition is defensible in isolation. Together, they add surface area that the team now has to maintain.

A useful review filter:
Would I have asked a junior developer to simplify this?
If yes, the same feedback applies to AI-generated code.

Test quality needs closer inspection. AI tools generate tests quickly, but the tests sometimes mirror the implementation rather than verify behavior. A test that passes because it asserts what the code does — rather than what the feature should do — gives false confidence.

// Weak: testing implementation
it('calls the save method on the repository', function () {
    $repo = Mockery::mock(UserRepository::class);
    $repo->shouldReceive('save')->once();
    // ...
});

// Better: testing behavior
it('persists the user and returns the created profile', function () {
    $response = $this->postJson('/api/users', [
        'name' => 'Satheez',
        'email' => 'satheez@example.com',
    ]);

    $response->assertStatus(201);
    $this->assertDatabaseHas('users', ['email' => 'satheez@example.com']);
});

The first test will pass even if the feature is broken, as long as the method is called. The second test will fail if the feature is actually broken. AI-generated tests often lean toward the first style because it is easier to generate from the implementation.

The author’s review responsibility

This is the uncomfortable part: when you use an AI tool to write code, your review responsibility increases, not decreases.

The temptation is the opposite. The code looks clean. The tests pass. The tool seemed confident. Submitting the PR feels like the natural next step.

But you are the author. Your name is on the commit. If the code has a subtle bug, a security gap, or a design problem, the review process should not be the first time someone thinks carefully about it.

Before submitting an AI-assisted PR, I run through this checklist:

1. Can I explain every file change without re-reading the diff?
2. Did I verify behavior, not just test output?
3. Are there changes I accepted because they looked reasonable but I did not fully trace?
4. Did the tool change anything outside the scope I intended?
5. Would I defend every design choice if asked in review?

If the answer to any of these is “not really,” the PR is not ready. The tool did its job. The author has not finished theirs.

The reviewer’s adapted checklist

For reviewers looking at PRs where AI tools were involved — and increasingly, that is most PRs — the review checklist needs a few additions.

Ask about scope drift. AI tools are helpful and thorough. That means they sometimes fix things you did not ask them to fix. A PR that started as “add validation to the email field” might include a refactored error handler, an updated test utility, and a renamed constant. Each change might be fine. Together, they make the PR harder to review and harder to revert.

Review question: Is every change in this diff necessary for the stated goal?

Check for phantom abstractions. AI tools love creating helper functions, utility classes, and wrapper layers. Sometimes these are useful. Sometimes they add indirection without adding clarity. If a new abstraction is only used once, it probably should not exist yet.

Verify that tests test the right thing. Skim the test names. Do they describe user-visible behavior or implementation details? A test called it calls the notification service is weaker than it sends a welcome email after registration. The first breaks when you refactor internals. The second breaks when the feature breaks.

Look for confident wrongness. AI-generated code does not hedge. It does not add a comment saying “I am not sure about this edge case.” It writes code that looks complete and authoritative even when it is making an assumption. The reviewer needs to supply the skepticism the tool does not have.

The conversation around AI-assisted code

Some teams are starting to require disclosure: “this PR was written with AI assistance.” I think that is reasonable but insufficient. The useful information is not whether AI was used — it probably was — but which parts the author verified deeply and which parts they accepted at face value.

A more useful convention:

## PR Description

**Goal:** Add rate limiting to the search endpoint.

**AI-assisted:** Yes — Claude Code for implementation and test scaffolding.

**What I verified manually:**
- Rate limit configuration matches the spec (60 requests/minute per API key)
- Error response shape follows our API envelope
- Existing search tests still pass

**What I want a reviewer to look at:**
- The middleware registration order — I am not sure if it conflicts with auth
- The test for the edge case where the API key is missing

That description is honest and useful. It tells the reviewer where to focus. It acknowledges that the author has high confidence in some areas and less in others. It treats the review as a collaboration, not a gate.

When review tooling helps

AI tools can also assist the review itself. Asking a tool to summarise a diff by behavior change is a reasonable first step:

Summarize this diff by behavior change.
Flag anything that changes public API, error handling, data persistence, or security.
Ignore formatting and import order changes.

That summary is a map, not a verdict. It helps the reviewer navigate a large diff. It does not replace reading the code.

The trap is using an AI tool to review AI-generated code without a human in the loop. That is circular trust. The same blind spots that produced the code can exist in the review. A human reviewer with domain knowledge is still the part of the process that catches the things neither tool thought to check.

The practical version

AI-generated PRs are not worse than human-written PRs by default. They are different. The code is usually clean, the tests usually pass, and the edge cases are usually where the problems hide.

For authors: review your own AI-assisted code more carefully than code you wrote by hand. You understand manual code because you made every decision. You understand AI-generated code only if you verified every decision.

For reviewers: adjust your attention. Spend less time on formatting and naming. Spend more time on scope, consistency, test quality, and confident wrongness. Ask the author what they verified and what they want you to check.

The goal is not to slow down AI-assisted development. It is to make sure the review process stays meaningful when the volume and origin of code changes.