AI coding assistants are fast. That's the problem.

Speed without verification is how you ship a Terraform plan with shell injection, database corruption on every deploy, and a monitoring alert that fires when nothing is broken. All in the same document. All from the same session. The AI didn't warn you. It felt productive right up until it wasn't.

That happened to me. What caught it wasn't a single code review. It was five of them — each looking at the code from a completely different angle.

the infra plan that almost destroyed everything

I was building a blue/green deployment system on GCP. FastAPI backend in Docker, SQLite state machine, nginx upstream swap between blue and green. I wrote a detailed plan. Thorough, well-reasoned, the kind of document you feel good about. Then I sent it to an adversarial reviewer.

The verdict: DO NOT SHIP. Eight critical issues.

Not "some concerns." Not "minor nits." Eight things that would have caused root compromise, data corruption, or a deployment that never starts. Here is a sample of what was found.

C2 — SQLite corruption on every deploy

The state Docker volume was mounted by both blue and green containers simultaneously during the 30-second health check window. Two concurrent SQLite writers, no locking coordination. Database corruption on every single deploy cycle. The plan didn't mention it. A standard code review wouldn't have caught it — it's a runtime interaction, not a code smell. You only see it when two processes race for the same file at the wrong moment.

C3 — Shell injection via Secret Manager

The deploy script loaded the environment with:

set -a; source service.env; set +a

source executes arbitrary bash. The env file content came from Secret Manager. If an attacker gets write access to Secret Manager — or if someone accidentally puts a bash expression in a value — that's container root. The plan had explicitly argued against storing credentials in Terraform state for security reasons, then introduced a worse vulnerability two sections later. The model that wrote it didn't notice the contradiction.

C6 — Containers couldn't read their own secrets

The env file was mounted chmod 600. The containers ran as a non-deploy UID. First deploy, health check fails, nothing starts, no useful error message. The kind of bug you spend an hour staring at before you realize the problem is a permission bit, not the code.

H18 — Alert fires when nothing is broken

COMPARISON_LT threshold=1 on REDUCE_COUNT_FALSE. This fires when zero regions are failing. The alert was configured backwards. An on-call engineer would have been paged whenever the system was healthy and left in silence when it wasn't.

A standard code review found none of this. The adversarial review found all of it — because it assumed everything was broken and worked to prove it, rather than assuming correctness and looking for exceptions. That posture change is everything.

"The AI didn't warn you. It felt productive right up until it wasn't. Speed without verification is just fast failure."

why one review isn't enough

The deeper issue is that a single reviewer, human or AI, brings a single posture. They read with a particular set of assumptions, a particular mental model, a particular definition of "what could go wrong." That mental model has blind spots. One reviewer's blind spots are not the same as another's — which is precisely why you need more than one.

AI makes this worse in a specific way: the model that wrote your code built the same mental model you did while writing it. When you ask it to review its own output, it reviews it from inside the same frame. It tends to approve what it generated because it generated it with an intent that still makes sense to it. The blind spots are shared. You need reviewers that weren't in the room when the code was written.

My workflow runs five review gates before anything significant ships. They are not redundant. Each one catches a different failure mode, asks a different question, and approaches the code with a different posture.

the five gates

Gate 1
/plan-eng-review
Is this the right design?

Engineering correctness before implementation. Schema design, missing edge cases, architecture decisions, API contracts. This gate catches "you've designed yourself into a corner" before you've written a line of code. A recent design doc for a multi-product pricing feature scored 6/10 here — with a blocking issue: the entire calculation engine was speculative because the actual pricing numbers hadn't been obtained yet. Three days of implementation work, avoided. The cost to fix a design flaw after the code exists is an order of magnitude higher than catching it in a doc review.

Gate 2
/codex
Does this do what it claims?

An independent AI perspective on the diff — not the same model that wrote the code reviewing it. Codex tends to catch logical gaps, overcomplexity, and "this doesn't do what you think it does" bugs. Running it on freshly written code surfaces disagreements between what the implementation does and what the spec said it should do. The disagreements are usually small. Small disagreements left unresolved become large incidents.

Gate 3
Greptile
How does this change ripple through the codebase?

Greptile reads the entire codebase, not just the diff. It catches cross-file regressions, surfaces callers of changed functions that weren't updated, finds invariants that hold in one file but break in another, and flags when a change quietly invalidates something documented in a completely unrelated part of the repo. The model that wrote the change doesn't know the codebase exists beyond the context window. Greptile does. On a large PR, this gate regularly finds two or three things that no amount of diff-level review would surface — because the problem isn't in the diff, it's in the gap between the diff and everything else.

Gate 4
/review
Is the code good?

The standard code quality pass. SQL safety, security at the obvious level, test coverage, style consistency. This is the review that most teams run and call sufficient. It catches what standard reviews catch. It is necessary but not sufficient — which is the entire point. The other four gates exist because this one, however good, cannot catch runtime interactions, cross-file regressions, design contradictions, or adversarial failure paths.

Gate 5
Adversarial review
How does this fail, and what does failure cost?

Maximum skepticism. Assumes everything is wrong until proven otherwise. Hunts for race conditions, data corruption paths, security exploits, silent failures, and logic errors that look correct in isolation but break under real conditions. This is where the shell injection gets found. This is where the concurrent writer problem surfaces. This is where the backwards alert comparator gets caught. It runs last on purpose — it is expensive, it requires reading the full implementation with hostile intent, and you don't run it on code that hasn't already survived the first four gates. It is the final filter, not the first one.

the pattern in practice

On a typical session, this is what shipping looks like in my prompts:

"commit and create PR — but first /plan-eng-review, /codex, Greptile, and /review"

Not optional. Not "if we have time." Before the PR exists.

On larger infrastructure changes, the adversarial review joins:

"/plan-eng-review, /codex, Greptile, /review, and adversarial review of the full phase"

All five, on the same diff, before it touches a branch. The sequence matters. Design review first — there is no point finding code-level bugs in a design that's wrong. Codex and Greptile next — independent perspectives, different blind spots. Standard review after — quality and style. Adversarial last — the final interrogation, performed on code that has already survived four rounds of scrutiny.

what this costs vs. what it saves

Running five review gates on every significant change adds roughly 10 to 20 minutes per session, depending on the size of the diff.

The alternative is the infra plan: eight critical issues including one that hands container root to anyone with Secret Manager write access, one that corrupts the database on every deploy, one that prevents anything from starting on first run, and one that pages on-call when the system is working correctly.

The math is not complicated. Find the shell injection in a review, or find it at 2am when something unexpected gets injected into your deploy environment. Find the concurrent writer problem in a review, or find it after three weeks of mysterious database corruption that you can't reproduce in dev because your dev environment only runs one container at a time.

Every gate has a price. Every uncaught bug has a much larger one.

the spec review variant

The gates don't only apply to code. Running adversarial review on a design document before implementation recently caught 14 issues in a spec for a new pricing feature. Among them:

A direct contradiction between two sections about what ships in Phase 1. Volume discount mechanics described but not specified — step discount or blended rate? TypeScript types referenced throughout but never defined anywhere. An effort estimate of three to four days that didn't account for the possibility that the actual pricing formula was structurally different from what the spec assumed.

None of those would have been caught by a code review. They would have been found during implementation, when fixing them means rewriting code that already exists, not amending a document that doesn't yet. The cost of a contradiction in a spec is one conversation. The cost of the same contradiction discovered mid-implementation is days.

"The model that wrote the code will often approve the code. It built the same mental model you did. It shares your blind spots. You need reviewers with different postures."

the rule

Never ship AI-generated code through a single review pass.

The model that wrote the code will often approve the code. It built the same mental model you did during the session. It shares your blind spots because it was present when you formed them. You need reviewers with different postures: one that checks whether the design is right, one that checks whether the implementation matches the intent, one that checks how the change lands across the whole codebase, one that checks code quality, and one that assumes failure and tries to prove it.

Five gates feels like overhead. It feels that way right up until the adversarial reviewer finds the thing that would have taken down production at the worst possible moment. Then it feels like it paid for itself for the next six months — and you run all five gates from that point forward without needing to be reminded why.