Profile
Back to NewsBack
Dev.to 8 min
Reader Mode
How to stop AI from confidently shipping broken code (a pattern that actually works)

How to stop AI from confidently shipping broken code (a pattern that actually works)

23 hours ago

The scariest diff passed every test — and would've lost a customer money.

The scariest diff AI ever handed me passed every test, read beautifully, and would have lost a paying customer their money the first bad night in production. It didn't look risky. It looked finished. That's the whole problem.

AI doesn't ship broken code because it's dumb. It ships broken code because it's confident, and almost nothing in a normal dev loop is trying to prove it wrong. You ask it to write the thing, it writes the thing, the tests go green, and every signal you have says ship. Confidence is the output. Correctness is a coincidence you're hoping rides along.

I spent 30 days letting AI write 100% of my code — refused to type a line of application logic myself — specifically to find out what actually stops this. Not the demos. The real thing, in production, with money on the line. Here's the honest answer: it wasn't a smarter model, and it wasn't a cleverer prompt. It was one structural rule, and you can adopt it today without changing a single tool.

Why "review this code" does nothing

The instinct, when you don't trust the output, is to ask the AI to check its own work:

"Review this code and tell me if it's correct."

This is worse than useless, because it feels like a safeguard while doing nothing. An LLM asked "is this correct?" is being asked to agree with itself, and it's exceptionally good at agreeing with itself. It grades its own homework in a slightly more formal voice and hands you back an A. You didn't add a check. You added a second layer of confidence on top of the first one.

The failure isn't that the model is bad at reviewing. It's that "confirm this" and "break this" are different jobs, and confirmation is the one thing the model will always deliver. You have to stop asking for the job you'll always get.

The pattern: the Refutation Gate

Here's the entire rule, and it's boring on purpose:

Nothing merges until a second reader — whose only job is to break it — has tried and failed.

Three parts make it actually work. Skip any one and it collapses back into an echo.

1. The reviewer is not the author

The thing that wrote the code cannot be the thing that blesses it — even if it's the same model, it has to be a separate, clean context that never saw the code get written. The author's context is poisoned: it already "knows" the code is right, because it just spent 4,000 tokens convincing itself. A fresh reviewer with no memory of the writing is the cheapest independence you can buy.

Better still: make the reviewer a different model family. Different training distribution, different blind spots. Two models from the same family share the same idea of what "clean" looks like — so they share the same landmines. The single highest-leverage change I made all month was pointing the reviewer at a different model than the author.

2. The reviewer gets a break-it brief, never a bless-it brief

This is the part that does the work. Don't ask the reviewer to review. Give it a job it can only do by finding the failure.

Instead of this:

Review this diff and tell me if it looks correct.

Do this:

This code is broken. I know it is — I just don't know how yet.
Your job is to produce the specific input, sequence, or state that
makes it fail. Assume:

- the network drops a packet at the worst possible moment
- two of these run at the same time
- the database write fails AFTER the external call succeeds
- the user does the thing no sane user would do

Give me the exact scenario that loses data or loses a customer money.
If you truly cannot find one, say so explicitly and explain what
would have to be true for that to be the case.

Notice what changed. The reviewer is no longer looking for reasons to say yes. It's hunting a specific, concrete failure, and its default is "this is broken — prove otherwise." Same model. Same weights. Completely different output, because the objective is the reviewer, not the model.

3. A human owns the merge

Not to out-code the machine — it out-codes you. To hold the one thing neither agent has: the memory of having been burned. More on this below, because it's the part that can't be automated and shouldn't be.

The bug it catches that everything else waves through

Let me make this concrete with the exact diff that started all of this.

The AI wrote a Stripe webhook handler. Simplified:

app.post('/webhook', async (req, res) => {
  const event = verify(req);
  res.sendStatus(200);        // tell Stripe "got it"
  await db.savePayment(event); // then write the row
});

Read it. It's clean. It's fast — it acks Stripe immediately so the webhook latency is low. Every test passes, every single time. If you asked an AI "is this correct?" it would say yes and compliment the low-latency acknowledgement. Mine did.

Now run it through the Refutation Gate. The break-it brief says "the database write fails after the external call succeeds." And there it is: it acknowledges the event before it persists it. One database blip in the gap between the 200 and the savePayment — and Stripe believes the event is delivered, your DB never heard about it, and a customer who just paid you has access to nothing and no record they ever paid. On a bad night, silently, to real people.

That bug is invisible to "review this." It is glaringly obvious to "produce the input that loses money." The code didn't change. The question did.

The fix is one line of ordering — persist first, ack after — but you only ever write that fix if something in your loop was actively trying to break the happy path. Confidence would have shipped it. Refutation caught it.

Why the human still can't leave the room

Here's the limit I hit, and I want to be honest about it instead of selling you a fully-automated fantasy.

I fed that exact webhook to a second AI with a perfect break-it brief — and when the author and reviewer were the same model family, it still sometimes approved it. Why? Because both models were trained on the same million examples of "clean webhook code," and both of them believed ack-before-persist was fine. The reviewer didn't refute the blind spot. It re-derived it, more confidently, and called it good taste.

Then I showed the same code to a senior engineer with no special prompt. Five minutes: "it acks before it writes — I got paged for exactly this in 2021, it's a nightmare to reconcile."

She wasn't smarter than the model. She'd been burned. She had scar tissue you cannot train into a context window, because the model has read a million descriptions of the dual-write problem and she has lived one. That's the difference, and it's the entire reason a human stays on the merge button: to catch the one bug that lives inside the machine's blind spot, which is exactly the bug the machine will confidently wave through.

The whole pattern, in one place

Steal this. It costs nothing and it's tool-agnostic:

  1. Never ask AI to confirm its own code. Confirmation is the one output it can't fail to produce, so it tells you nothing.
  2. Add a second reader in a clean context — different model family if you can. Independence is divergence, not a second seat.
  3. Give that reader a break-it brief, not a bless-it brief. "Produce the input that loses money," never "does this look right."
  4. Keep a human — ideally a burned one — on the merge. Their job isn't to write better code. It's to remember what hurt.

A confident AI is not a checked AI. The only thing that turns confidence into correctness is something whose entire job is to try to prove it wrong.

That, exactly, is the shape of what we build at xenition: an agent that does the work, a different agent whose only job is to tear it down, and a person who owns the merge. I didn't start from that thesis. I arrived at it from 30 days of watching confident, beautiful, green-checkmarked diffs that would have quietly hurt real people — and finding the one pattern that caught them.


Questions I'd genuinely like answered in the comments:

  1. What's the confident-but-broken diff AI handed you — the one that passed every test and would've bitten you in prod?
  2. Do you run an AI reviewer? Is it the same model as your author, and have you ever measured what a different family catches?
  3. Has anyone A/B'd "review this code" against a break-it brief on the same diff? I want numbers.
  4. If AI does all the entry-level work, where does the next senior's scar tissue come from — the scar tissue that catches what the agents wave through?
Chat with me