# Reviewing AI-Generated Pull Requests: A Checklist That Catches Real Bugs

> Source: <https://dev.to/muhammadalimughal/reviewing-ai-generated-pull-requests-a-checklist-that-catches-real-bugs-311h>
> Published: 2026-11-14 07:14:38+00:00

In September a client's team merged a 900-line pull request that added CSV export to their Laravel admin. The code was clean, typed and well commented. Two reviewers approved it in under twenty minutes. A week later the finance lead noticed exports were missing rows: the AI-generated query used `chunk()` while ordering by a column it was also updating, so pages shifted under it. Nobody had read the query closely, because everything around it looked so professional. That incident changed how we review code, and it is the reason for this series.

I wrote this with help from AI tools and checked every snippet, config and checklist item myself. I am a Principal Software Engineer in Karachi with 9+ years across PHP/Laravel, Vue, Node and .NET microservices, and through Loop Origin I maintain production SaaS codebases where a growing share of code is AI-assisted.

**📚 Series Navigation — Maintaining AI-Written Code in Production**

**Part 1 (This Post):** Reviewing AI-Generated Pull Requests: A Checklist That Catches Real Bugs
**Part 2:** Characterization Tests Before You Refactor AI-Written Code
**Part 3:** Hallucinated Packages & Dependency Drift in AI-Assisted Code
**Part 4:** Static Analysis Gates: PHPStan, TypeScript Strict & Coverage in CI
**Part 5:** Decision Records So AI Context Doesn't Rot
**Part 6:** Measuring AI Coding Impact With DORA Metrics, Not Vibes
*Related earlier posts:* [Synthetic Checks & SLOs for the Paths Nobody Clicks](https://dev.to/muhammadalimughal/synthetic-checks-slos-for-the-paths-nobody-clicks-4hhl) · [Config & Feature Flags That Quietly Diverged Across Environments](https://dev.to/muhammadalimughal/config-feature-flags-that-quietly-diverged-across-environments-4m38) · [Webhooks That Stopped Arriving](https://dev.to/muhammadalimughal/webhooks-that-stopped-arriving-gap-detection-reconciliation-jo7)

**Direct answer:** Review AI-written code for behaviour, not style. Keep PRs small, require the author to explain intent and test evidence, let CI enforce formatting, types and dependency checks, and spend human attention on data access, edge cases, security boundaries and error handling. Polished code earns no trust by default; the author still owns every line.

The trending DEV post "AI Code Is Unmaintainable? So Is Ours." makes a fair point: human code was never perfect either. But the failure pattern is different. Human code that is wrong usually looks wrong: messy, inconsistent, half-finished. AI code that is wrong looks finished. That flips the reviewer's instinct. Over the last year on our projects I tracked where review misses came from, and the categories were consistent:

| Bug class | Typical AI-written example | Why reviewers miss it | Best catch | 
|---|---|---|---|
| Data access | N+1 queries, `chunk()` with mutating order, missing tenant scope | Code reads naturally; bug is in runtime behaviour | Query log in tests, human review | 
| Edge cases | Empty arrays, nulls, timezones, currency rounding | Happy path is complete and well named | Property or table-driven tests | 
| Invented APIs | Calls a method or config key that doesn't exist in your version | Looks plausible; IDE may not flag dynamic calls | Static analysis, CI run | 
| Dependencies | New package added for a one-liner, or a misspelled name | Lockfile diffs get skipped | Dependency review in CI | 
| Security boundaries | Missing authorization check, mass assignment, raw SQL | Controller "works" in a demo | Checklist + policy tests | 
| Error handling | Broad `catch` that logs and continues | Looks defensive and responsible | Review for swallowed errors | 

Formatting, naming and lint issues barely appear on this list. Machines should catch those. Humans should spend their twenty minutes on the top two rows.

Our PR template now asks for three things before anyone else looks:

``` php
## What and why
<!-- One paragraph in your own words. Not generated. -->

## How I verified it
- [ ] Ran locally against realistic data (row counts / tenants: ___)
- [ ] Tests added or updated: ___
- [ ] Query count checked for list/export endpoints

## AI assistance
- [ ] Parts generated with AI: ___
- [ ] I have read and can explain every generated line
- [ ] No new dependencies, or each one is justified below
```

The "in your own words" line matters. If the author cannot explain the change without pasting model output, the PR is not ready. Teams usually push back for a week and then admit it saves time.

```
# .github/workflows/pr-checks.yml (excerpt)
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with: { php-version: '8.3' }
      - run: composer install --no-interaction --prefer-dist
      - run: vendor/bin/pint --test                 # formatting
      - run: vendor/bin/phpstan analyse --memory-limit=1G   # invented methods, types
      - run: php artisan test --parallel
  deps:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v4
        with: { fail-on-severity: moderate }
```

PHPStan at level 6 or higher catches a surprising number of invented methods and wrong argument types before a human looks. Dependency review flags new packages and known-vulnerable versions, which Part 3 covers in depth.

AI makes it cheap to produce 1,000-line PRs, and review quality collapses above a few hundred lines. We fail a soft check above 400 changed lines (excluding lockfiles and generated files) and ask authors to split. Exceptions are allowed with a reason in the description. The CSV export bug lived in a 900-line PR; split into "query + tests" and "UI + controller", the query would have had a reviewer's full attention.

This is what I actually look at, in this order:

`$fillable`), no raw SQL with interpolated input.
For the export bug, item 1 would have prompted the fix:

```
// Before: rows skipped because 'exported_at' changes while chunking ordered results
Invoice::where('tenant_id', $tenantId)->whereNull('exported_at')
    ->orderBy('exported_at')->chunk(500, fn ($rows) => $this->write($rows));

// After: stable ordering by primary key, safe while updating rows
Invoice::where('tenant_id', $tenantId)->whereNull('exported_at')
    ->chunkById(500, fn ($rows) => $this->write($rows));
```

If your team needs help setting up this kind of review process on an existing codebase, that is a big part of what we do in [Laravel development](https://looporigin.com/services/laravel-development?utm_source=devto&utm_medium=series&utm_campaign=review-ai-generated-pull-requests) and [custom web application development](https://looporigin.com/services/custom-web-application-development?utm_source=devto&utm_medium=series&utm_campaign=review-ai-generated-pull-requests) engagements.

No. Bans push usage underground. Require disclosure and ownership instead; the author is responsible for every line, whoever typed it.

AI pre-review is useful for style, missing tests and obvious mistakes. It does not replace a human who knows the domain, the data and the business rules.

Smaller PRs, a checklist that starts with data access and authorization, and tracking which bugs escaped review so the team sees the pattern.

Start at the highest level your codebase passes today, add a baseline file, and raise it gradually. New code must pass the higher level.

For the first two weeks, slightly. After that, fewer rollbacks and hotfixes usually make delivery faster overall.

Split them into mechanical changes and behaviour changes, and add characterization tests first. Part 2 covers exactly that.

Next in this series, Part 2 covers characterization tests before you refactor AI-written code.
