← All lessons

Putting an AI Reviewer in the Merge Gate

A Comment Nobody Reads vs. a Gate Nobody Can Skip

The easy version of AI code review is a bot that posts comments on a pull request. It feels productive. It is mostly theater. Advisory comments get skimmed, thumbed-down, or quietly ignored, and within a couple of weeks the team has trained itself to scroll past them the way we all scroll past a linter warning we’ve decided doesn’t matter.

The version I actually wanted was different: the AI review is a blocking check. Every finding it raises becomes a discussion thread that has to be resolved — fixed, or explicitly dismissed with a reason — before the branch can merge. Not advisory. Load-bearing.

That one design decision — advisory to blocking — is where all the interesting problems live. The moment a machine’s opinion can stop a merge, you are forced to answer questions the comment-bot version lets you dodge. Is the review even repeatable? What happens when it’s wrong? What does it cost? And who, exactly, is accountable for the code that ships?

Here’s what I learned answering those.

The AI Reviews What the Machines Structurally Can’t

The first mistake is asking the model to do a linter’s job. Don’t. You already have tools that are faster, cheaper, and deterministic at the things they check: formatters, static analysis, type checkers, test runners, spec-drift detectors. If a rule can be expressed mechanically, express it mechanically and let it fail the build with a precise line number. That’s your machine gate, and it should run first.

The AI belongs after that gate, aimed squarely at what the mechanical tools cannot see:

  • Does this change actually do what the ticket says, or something adjacent?
  • Does it follow the conventions of this codebase — not generic best practices, but how we do things here?
  • Did the docs, the changelog, the tests drift out of sync with the behavior?
  • Is there a correctness risk that’s obvious to a careful reader but invisible to a type system?

I think of it as two layers with a clean division of labor: the mechanical layer proves the structurally-checkable, and the judgment layer reviews the rest. When the two get blurred — when you let the model re-flag formatting, or you expect a linter to catch a logic error — both layers get worse. Keep them separate and each does its job well.

A practical consequence: the model’s prompt should tell it what not to look at as clearly as what to look at. “These tools already cover X, Y, Z — don’t re-report them” is one of the highest-leverage lines in the whole system.

The Uncomfortable Part: It Isn’t Deterministic

Here’s the thing nobody warns you about. Run the same diff through the same model twice and you will not get the same review. Different findings, different phrasings, sometimes a real bug caught on one pass and missed on the next.

I confirmed this the unglamorous way: I ran the same change through review several times and diffed the results. One of the passes surfaced a genuine defect the others didn’t. Same input, same model, different output.

For an advisory comment, non-determinism is a shrug. For a gate, it’s a crisis of legitimacy — because it means a single pass is not a guarantee, it’s a sample. You are drawing one review from a distribution of possible reviews, and the good findings are not guaranteed to be in the draw you got.

Two things follow. First, stop pretending a green review means “no problems” — it means “this particular sample found nothing,” which is weaker. Second, if a finding matters enough to block on, it can be worth running more than one pass and taking the union of what they find. Redundancy is how you buy back some of the reliability that determinism would have given you for free. It costs more. Which brings us to the next lesson.

Cost Is a First-Class Design Constraint, Not a Footnote

Every review is metered. You pay per token, and the token count scales with the size of the diff and the number of times you trigger it. That second factor sneaks up on people. A pipeline that reviews on every push, on an iteration-heavy day where someone pushes twenty times to chase a flaky test, will happily spend real money reviewing near-identical diffs over and over.

I learned to treat cost the way I treat latency or memory — as a budget you design against, not a bill you’re surprised by:

  • Pick the model deliberately. The top-tier model is not always the right call. For review specifically, a mid-tier model gave me most of the quality at a fraction of the price. Review is a judgment task with a bounded output, not open-ended generation — it’s exactly where a cheaper model holds up well.
  • Make the cost model legible. Roughly, cost ≈ diff size × number of pushes. Once the team can see that, behavior changes on its own.
  • Build the discipline into the workflow. “Batch your pushes” stops being nagging and becomes a line item people understand once they know each push is a metered review.
  • Cap the spend. A runaway loop should hit a ceiling and fail safe, not silently drain a budget.

None of this is exotic. It’s the same engineering maturity you’d apply to any metered dependency. The mistake is treating the LLM as free-ish because a single call is cheap. At the scale of “every merge request, forever,” cheap-per-call is a budget, and budgets need owners.

Fail Loud, Because a Skipped Review Is a Silent Bypass

Ask yourself what happens when the review can’t run. The API is down, the key is throttled, the container fails to start. If your pipeline treats “review didn’t run” the same as “review passed,” you have quietly built a bypass: any time the check fails to execute, code sails through ungated.

So the rule I settled on: a review that cannot run must fail loudly — mark the request as unreviewed, alert a human, and refuse to treat the absence of findings as the absence of problems. A silent skip is the one failure mode that erodes the whole gate, because it’s invisible exactly when it matters.

The same instinct applies to what you send. The review needs your code, but it does not need your secrets. Scrub environment files and credentials out of whatever you hand to the model before it leaves your infrastructure. Treat the review payload like any other outbound data flow — with intent.

The Model Never Approves. Humans Do.

This is the line I hold hardest, and it’s a leadership decision more than a technical one.

The AI can find. It can block. It can raise a thread that has to be dealt with. What it does not do is approve. Approval — the act of saying “yes, this is good, I’m accountable for it merging” — stays with a person, always.

The reason is not sentimentality about human judgment. It’s about where accountability lives. The most dangerous outcome of a good AI reviewer isn’t that it’s wrong occasionally — it’s that it’s right often enough that people stop reading and start rubber-stamping. Automation complacency is a well-documented failure mode in every field that has tried to automate judgment, and code review is not special. If the machine’s blessing is sufficient to merge, you have trained your engineers to outsource the one thing you most need them to keep doing: understanding the code they own.

So the gate is deliberately asymmetric. The AI can stop code from merging. It cannot, by itself, let code merge. That asymmetry keeps a human in the loop precisely where the stakes are highest, while still capturing the enormous value of a tireless reviewer that reads every single diff.

What It Actually Buys You

Done well, this doesn’t replace your reviewers — it changes what they spend their attention on. The tedious, mechanical, “you forgot to update the changelog” layer is handled before a human looks. The reviewer arrives at a pull request where the structural stuff is already clean and the AI has flagged the spots worth a second read. Their scarce, expensive attention lands on the things only a human should decide.

That’s the whole game. Not “AI reviews the code so we don’t have to.” Rather: the machine handles what’s checkable, the AI surfaces what’s worth a look, and the human owns the decision — with each layer doing only what it’s actually good at.

The comment-bot version of AI review asks nothing of you and delivers about as much. The gate version asks you to be honest about determinism, disciplined about cost, paranoid about silent failure, and clear about accountability. That’s more work. It’s also the difference between a novelty and a system you’d trust in production.

← All lessons