Summary
AI coding assistants and agents now write a large share of the code that reaches pull requests. Sonar's latest State of Code developer survey reports that 76% of developers and engineering managers say they generate AI code faster than they can verify it. Writing code is no longer the bottleneck; reviewing it is.
This guide is a practical workflow for reviewing AI-generated code without slowing your team to a crawl or shipping subtle bugs. It is written for CTOs and team leads running Laravel, PHP and Node.js products, but the ideas apply to any stack. The short version: let machines check what machines are good at, make the AI explain its own change, keep pull requests small, and spend human attention on intent, data and security.
Why AI-generated code needs a different review process
Code written by a model fails in different ways from code written by a tired human. A human who doesn't understand a requirement usually writes something visibly incomplete. A model that doesn't understand it writes something complete, confident and wrong. Typical patterns we see when reviewing AI-assisted pull requests:
- Plausible but invented APIs. A method that "should" exist on an Eloquent model, a Laravel helper from an older version, or an npm package name that doesn't exist (or worse, one that does and was registered by an attacker).
- Silent scope creep. The agent was asked to fix one validation rule and also "tidied up" three unrelated files, renamed a column accessor and changed a default.
- Happy-path tests. Tests that pass because they assert what the code does, not what the business needs. Sometimes the agent edits a failing test until it passes.
- Missing authorisation. New endpoints that validate input perfectly but never check whether the current user may touch the record.
- Performance traps. N+1 queries, loading whole tables into memory, or synchronous calls to slow third-party APIs inside a request.
- Volume. Agents produce large diffs quickly, and large diffs get skimmed rather than reviewed.
None of this means AI code is bad. It means the review process has to assume the author is fast, fluent and has no memory of your business rules unless you give it to them.
The four review layers
Think of review as four layers, each catching a different class of problem. The cheaper layers run first and automatically, so human reviewers only see changes that have already passed the boring checks.
- Deterministic checks in CI: formatting, static analysis, type checks, tests, dependency and secret scanning.
- An AI first-pass reviewer that reads the diff in the context of the repository and leaves inline comments.
- The author's own verification, including a written explanation of what changed and why, and proof it was run.
- Focused human review on intent, data integrity, security and architecture.
Layer 1: make CI strict enough that AI can't talk its way past it
Static analysis is the most underrated defence against AI mistakes, because invented methods and wrong types are exactly what it catches. For a Laravel project, a sensible baseline is Laravel Pint for style, PHPStan with Larastan at a meaningful level, and your test suite with coverage reporting. For Node.js and TypeScript, use strict TypeScript, ESLint and your test runner.
# .github/workflows/ci.yml (Laravel example, trimmed)
name: ci
on: [pull_request]
jobs:
php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
- run: composer install --no-interaction --prefer-dist
- run: vendor/bin/pint --test
- run: vendor/bin/phpstan analyse --memory-limit=1G
- run: php artisan test --parallel
- run: composer audit
For a TypeScript service, turn on the compiler options that catch the sloppy patterns models tend to produce:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"exactOptionalPropertyTypes": true
}
}
Add npm audit or an equivalent dependency scanner, a secret scanner, and a lockfile check so that a new package can't appear without someone noticing. If you haven't hardened your dependency pipeline yet, our guide to securing Node.js and Laravel dependencies against supply-chain attacks covers the details.
One rule matters more than any tool: a pull request that changes or deletes existing tests must say why. You can enforce this with a CODEOWNERS entry on your test directories so a senior engineer is always asked to review test changes.
Layer 2: an AI reviewer as the first pass
Using AI to review AI code sounds circular, but it works well as a first pass because the reviewer runs with different instructions and fresh context. Several products now do this inside GitHub, GitLab or Bitbucket, including GitHub Copilot code review, CodeRabbit, Greptile, Qodo and Atlassian's new Code Reviewer for Jira users. They differ mainly in how much repository context they index, how configurable their rules are, and whether they can run self-hosted.
Whichever you choose, the setup is what makes it useful:
- Write your rules down. Most tools read repository instruction files. An
AGENTS.mdat the root is increasingly supported across tools, and GitHub Copilot also reads.github/copilot-instructions.md. Put your real conventions there: "all controllers authorise through policies", "money is stored as integer minor units", "never call external APIs inside a database transaction". - Tune for signal. Turn off style nitpicks that your linter already covers. A reviewer that leaves twenty trivial comments trains people to ignore it.
- Treat findings as suggestions. The AI reviewer is a smoke detector, not a judge. A human still decides.
A short example of review rules for a Laravel SaaS:
# AGENTS.md (review section)
- Every new route must have authorisation (Policy, Gate or middleware). Flag any that don't.
- Queries inside loops are N+1 risks: suggest eager loading.
- Tenant-scoped models must use the BelongsToTenant trait; flag raw queries that skip it.
- Monetary values are integers in minor units. Flag floats used for money.
- Do not add new Composer or npm dependencies without a note in the PR description.
Layer 3: make the author (human or agent) prove the change
The single biggest improvement most teams can make is a pull request template that forces explanation. When an agent opens the PR, ask it to fill this in; when a developer used an assistant, they fill it in themselves.
## What changed and why
(Link the ticket. One paragraph in plain language.)
## How it was verified
- [ ] Ran locally / in preview environment (link or screenshot)
- [ ] New or changed tests listed below, with what each one proves
- [ ] Edge cases considered: empty input, unauthorised user, large data, retries
## AI assistance
- Tool(s) used:
- Parts written mainly by AI:
- Anything the author is unsure about:
## Risk
- Touches auth, payments, migrations or personal data? Yes / No
The "anything the author is unsure about" line is gold. It tells the reviewer exactly where to look, and it gives developers permission to admit they accepted code they don't fully understand, which is far better than hiding it.
Layer 4: where human reviewers should spend their time
With layers 1 to 3 in place, human review becomes shorter and sharper. Focus on the questions only someone who knows the product can answer:
- Is this the right change? Does it solve the ticket, or a slightly different problem the model found easier?
- Who can do this? Check authorisation and tenant isolation on every new query, route, job and webhook handler.
- What happens to the data? Migrations, backfills, deletes, money and anything irreversible. Our guide to zero-downtime migrations in Laravel is a useful checklist here.
- What happens when it fails? Timeouts, retries, duplicate events and partial failures. AI code often has no answer to these.
- Will we understand it in six months? Clever, dense code generated in seconds can cost hours later.
A concrete Laravel example
Here is the kind of controller method an assistant might produce for "let users download an invoice PDF":
public function download(int $id)
{
$invoice = Invoice::findOrFail($id);
return response()->download(
storage_path('app/invoices/' . $invoice->file_name)
);
}
It compiles, it passes a happy-path test, and it looks clean. A focused review catches two serious problems: any logged-in user can download any invoice by changing the ID (a classic broken object-level authorisation bug), and the file path is built from a database value without checking it stays inside the invoices directory. The reviewed version:
public function download(Invoice $invoice)
{
$this->authorize('view', $invoice);
abort_unless(
Storage::disk('invoices')->exists($invoice->file_name),
404
);
return Storage::disk('invoices')->download(
$invoice->file_name,
"invoice-{$invoice->number}.pdf"
);
}
And the test that actually proves the business rule:
it('does not let a user download another customer\'s invoice', function () {
$owner = User::factory()->create();
$intruder = User::factory()->create();
$invoice = Invoice::factory()->for($owner)->create();
$this->actingAs($intruder)
->get(route('invoices.download', $invoice))
->assertForbidden();
});
This is the pattern to look for: AI code tends to be correct for the user who is allowed to do something and silent about the user who isn't. The OWASP API Security Top 10 is a good mental checklist for these reviews.
A Node.js example: retries and idempotency
Ask an assistant to "handle Stripe webhooks" in Express and you will often get a handler that verifies the signature correctly and then processes the event immediately. What's usually missing is idempotency: payment providers can deliver the same event more than once, so the handler must ignore duplicates.
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET
);
// Record the event id first; a unique index makes duplicates fail fast.
const inserted = await db.query(
'INSERT INTO processed_events (id) VALUES ($1) ON CONFLICT DO NOTHING RETURNING id',
[event.id]
);
if (inserted.rowCount === 0) return res.sendStatus(200); // already handled
await queue.add('stripe-event', { id: event.id, type: event.type });
res.sendStatus(200);
});
Reviewers should ask "what if this runs twice?" on every webhook, job and message consumer. It's a question models rarely ask themselves.
Keep AI pull requests small
Review quality drops sharply as diffs grow, and agents make it effortless to produce huge ones. Practical limits that work for many teams:
- Aim for pull requests a reviewer can understand in 15 to 20 minutes. Split larger work into a stack of smaller PRs.
- Separate mechanical changes (renames, formatting, dependency bumps) from behaviour changes.
- Tell agents explicitly: "only change files needed for this ticket; list any other improvements as suggestions instead of making them".
- Use preview environments so reviewers can click through the change instead of imagining it.
How to tell if your review process is working
Avoid measuring lines of code or the number of PRs merged; AI inflates both. Better signals are pull request cycle time (open to merge), change failure rate (deploys that cause an incident or rollback), escaped defects per release, and how many AI reviewer comments lead to an actual code change. If cycle time drops but change failure rate rises, your team is reviewing less, not reviewing better.
Common mistakes
- Banning AI tools. Developers use them anyway, just without guardrails. Set rules instead.
- Letting AI approve AI. An AI reviewer should never be the only approval on a merge to your main branch.
- No written conventions. If your rules live only in senior engineers' heads, neither the coding agent nor the AI reviewer can follow them.
- Ignoring juniors. When AI writes and reviews the code, junior developers can lose the feedback they learn from. Keep some review conversations human and explanatory.
FAQ
Should we trust AI code review tools to approve pull requests?
No. Use them as a fast first pass that catches common bugs and rule violations, and keep at least one human approval for merges to your main or production branch.
Which AI code review tool is best in 2026?
It depends on your Git platform, repository size and privacy needs. GitHub-centred teams often start with Copilot code review; tools like CodeRabbit, Greptile and Qodo add deeper indexing, cross-repo context or self-hosting. Trial two on real PRs before deciding.
How do we stop AI from inventing packages or APIs?
Strict static analysis and type checking catch invented methods, and a lockfile or dependency review step catches new packages. Require every new dependency to be justified in the PR description.
Does reviewing AI code take longer than reviewing human code?
Per line it can, because the code looks fluent even when it is wrong. With strict CI, an AI first pass, small PRs and a clear PR template, total review time usually becomes manageable.
What should a PR template for AI-assisted code include?
What changed and why, how it was verified, which parts were mainly AI-written, what the author is unsure about, and whether it touches auth, payments, migrations or personal data.
Can DayDecoder audit AI-generated code in an existing product?
Yes. We review Laravel, PHP, Node.js and React codebases for security, data integrity and performance issues, and help teams set up CI checks and review workflows for AI-assisted development.
- #AI Code Review
- #AI Coding Agents
- #Application Security
- #CI/CD
- #Code Quality
- #Developer Productivity
- #Laravel
- #Node.js
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash