Duskel Start a project
Blog/Product Engineering

How to fix AI-generated code that broke in production

You shipped fast with AI codegen and it worked — until it didn't. Here are the failure patterns a senior team keeps fixing in vibe-coded apps, how a rescue actually runs, and when to refactor versus rebuild.

Duskel·3 Sept 2026·10 min read·Product Engineering

AI codegen made the first version cheap. A founder describes a feature, the model writes it, it runs, the demo lands, the product ships. For a while that speed feels like a superpower. Then the app hits real users, real data, and real load, and the same speed starts working against you: something breaks in production, and nobody on the team can fully explain why, because nobody actually wrote the code.

2026 has been widely called the year the bill came due on that speed — industry commentators keep describing a wave of technical debt from a year of shipping code faster than anyone could review it. The pattern we see when these codebases land on our desk is remarkably consistent. It is almost never one exotic bug. It is a handful of the same failure modes, repeated across the whole app, because the model made the same reasonable-looking mistake every time it was asked a similar question. This is a field guide to those patterns and to how a senior team gets a vibe-coded codebase back under control.

The failure patterns a senior team keeps fixing

AI-generated code is usually not wrong in the way a beginner's code is wrong. It compiles, it is tidy, it follows conventions, and the happy path works. What it lacks is everything that only matters when reality stops cooperating — and that is exactly the part a model can't feel the absence of. Here is what we find, in rough order of how often it hurts.

  • Security holes. Missing authorization checks are the big one — an endpoint that trusts the user ID in the request instead of the session, so anyone can read anyone's data. Alongside them: secrets committed straight into the repo, SQL built by string concatenation, unvalidated input, and other OWASP Top-10 staples. Industry reviews of AI-generated code keep finding a meaningful share of it carries at least one of these, because the model optimises for code that works, not code that resists an attacker.
  • No tests, so nothing is safe to change. Vibe-coded apps almost always arrive with zero real test coverage. Every fix becomes a gamble, because there is no way to prove you didn't break something three files away. This is usually the single biggest reason the codebase feels stuck.
  • Nothing handles the unhappy path. The API call that fails, the payment that times out after it succeeded, the empty list, the malformed upload — the code assumes none of these happen. In a demo they never do. In production they happen hourly, and each one is an unhandled exception or a silent corruption.
  • N+1 queries and other performance cliffs. The model writes a loop that queries the database once per item because that reads cleanly. With ten rows it's invisible; with ten thousand it's a timeout. Missing indexes, refetching the same data, and loading entire tables into memory are the usual companions.
  • Tangled architecture that blocks the next feature. Logic copied inline instead of shared, business rules smeared across controllers and components, no clear boundaries. Each AI-generated feature was locally sensible and globally inconsistent, so adding anything new means touching ten places and hoping.
  • No observability. When something breaks in production there are no logs worth reading, no error tracking, no metrics. You find out from a customer, and then you're debugging blind because the system never recorded what it was doing when it fell over.

The tell that a codebase is AI-generated rather than junior-written: the mistakes are consistent. A human makes different errors in different places. A model makes the same reasonable-looking error everywhere it was asked a similar question — which is bad news for the bug, and good news for the fix, because one corrected pattern often closes dozens of instances at once.

How a rescue actually runs

Panic-fixing a vibe-coded app one bug at a time is how you spend a month and end up with a codebase that's just as fragile and now also half-rewritten in a second style. A rescue has an order to it, and the order matters more than the speed. Here is the sequence we run.

  1. Audit. Before touching anything, map what exists: the real architecture (not the intended one), where the data lives, which paths handle money or personal data, and where the security and stability landmines are. The output is a prioritised list — what will hurt you soonest, ranked ahead of what merely offends good taste.
  2. Stabilise the bleeding. Fix the issues that are actively dangerous or actively down first — the missing auth check, the committed secret, the query that's timing out under load. Add error tracking and basic logging early, even before the deeper fixes, so you stop debugging blind and start seeing what production is actually doing.
  3. Make it safe to change. Add a test harness and CI. You don't chase 100% coverage — you write tests around the critical paths and the parts you're about to touch, so every subsequent change is verified instead of hoped. This is the step that converts a frozen codebase back into one you can move in.
  4. Refactor behind the tests. Now that changes are provable, collapse the repeated patterns: pull duplicated logic into shared code, fix the N+1s, introduce the boundaries the architecture was missing. Because the model repeated itself, fixing one pattern well fixes it everywhere — this is where a tangled app starts feeling like software again.
  5. Harden and hand back. Close the remaining performance cliffs, finish observability, document what changed and why, and leave the team able to keep building on it — not dependent on us to touch it. A rescue that ends with you needing the rescuer forever isn't finished.

Refactor or rebuild?

This is the question every rescue turns on, and the honest answer is that it depends on how much of the code is worth keeping. The instinct to throw it all away and start clean is usually wrong — a full rewrite discards the working parts along with the broken ones, and rewrites have their own well-documented graveyard. But sometimes the foundation genuinely can't bear weight. Here is roughly how we call it.

SignalLean refactorLean rebuild
Core structureMessy but coherent; the data model roughly holdsData model is fundamentally wrong; fixing it touches everything
Feature velocitySlow but possible to add featuresEvery change breaks two other things; the team is fully stuck
Security & correctnessA finite list of holes to closeInsecure by design — auth and data access would need re-architecting anyway
How much is salvageableMost of it works; the problems are patterns, not foundationsThe parts that work are the minority

The middle path wins most often: refactor the codebase as a whole, and rebuild only the specific subsystem that's rotten — the auth layer, the billing flow, the one data model that was wrong from the start. Wholesale rebuilds are the expensive answer to a problem that's usually local. When a rebuild genuinely is the call, it's worth treating it as an MVP-style rebuild with a defined scope, not an open-ended rewrite.

What a rescue engagement looks like

Rescues price on the state of the code, not on a line count, because two apps of the same size can be a week apart or a quarter apart depending on how deep the problems go. That's why we start with the audit as a small, fixed piece of work: it's the only honest way to quote the rest. Broadly, engagements land in three shapes.

EngagementShapeWhat it covers
AuditFixed, shortA senior read of the codebase: the real architecture, the security and stability risks ranked by urgency, a refactor-vs-rebuild call, and a costed plan for the rest. Useful on its own even if you take it in-house afterwards.
StabiliseScoped projectClose the dangerous holes, add error tracking and CI, put tests around the critical paths, fix the worst performance cliffs. Gets the app safe and unstuck without a full overhaul.
Rescue + rebuildLarger project or retainerThe full sequence, including rebuilding the subsystems that are past saving, and handing back a codebase the team can keep building on. For apps where the debt is deep or the product is core to the business.

Be wary of anyone who quotes a rescue firm before reading the code. Nobody can price fixing a vibe-coded app from the outside — the whole point is that the problems are invisible until you open it. A cheap fixed quote sight-unseen usually means the fixing stops the moment the budget runs out, holes and all.

Why work with Duskel

We've been a founder-led software studio since 2021, with 40-plus builds shipped and our own products in production — which means we've lived the failure modes in this post from the inside, not just cleaned them up for other people. That's the relevant experience for a rescue: knowing which missing auth check, which N+1, which untested path is the one that actually bites, so you're not paying us to rediscover it.

A rescue is full-stack web development at its least glamorous and most valuable — it's senior work, and we keep the team senior, so you deal with the people fixing the code rather than an account manager relaying to a sub-contractor. And we'll tell you honestly when a refactor beats a rebuild, because talking you out of the more expensive option is part of being worth hiring. If your AI-built app is breaking under real users, send us the codebase and what's going wrong and we'll tell you straight what it needs.

FAQ

Should we refactor the AI-generated code or rebuild from scratch?

Usually refactor. A full rewrite throws away the working parts along with the broken ones, and rewrites have a long history of going over time and budget. The right call in most rescues is to refactor the codebase as a whole and rebuild only the specific subsystem that's genuinely past saving — the auth layer, the billing flow, the one data model that was wrong from the start. A rebuild is the right answer when the core data model is fundamentally wrong or the app is insecure by design, because fixing either touches everything anyway. The audit is what tells you which situation you're in.

How much does it cost to fix an AI-generated codebase?

It depends entirely on how deep the problems go, which is why we start with a short, fixed-price audit rather than quoting the whole thing blind — two apps of the same size can be a week apart or a quarter apart. From the audit you get a costed plan: a scoped stabilisation project to close the dangerous holes and add tests and CI, or a larger rescue-and-rebuild for apps where the debt runs deep. Be cautious of any firm that quotes a full rescue before reading the code; the problems that set the price are invisible from the outside.

How long does a rescue take?

The audit is short — days, not weeks. A stabilisation pass that closes the security holes, adds error tracking and CI, and puts tests around the critical paths is typically a matter of weeks. A deeper rescue that also rebuilds rotten subsystems runs longer and is better structured as a project or retainer. The honest answer for your specific app comes out of the audit, because timeline tracks the severity of the problems, not the size of the codebase.

Can you work with our existing codebase, or do you have to start over?

We work with what you have. The whole point of a rescue is to keep the parts that work and fix the parts that don't, rather than discarding a year of shipped features. We read the real architecture first, then refactor behind a test harness so every change is verified, and we only rebuild the specific pieces that genuinely can't be saved. Starting over is the last resort, not the opening move.

Do we keep the code and the ability to maintain it ourselves?

Yes. It's your code and it stays your code. A rescue that ends with you permanently dependent on the rescuer hasn't done its job. We hand back a codebase with tests, CI, and documentation of what changed and why, so your team can keep building on it without us. If you want ongoing help you can have it, but the goal is to leave you able to move on your own.

Written by Duskel

A software studio that ships and maintains its own products — KeepChats, Gwora and Cairn — and builds the same way for clients. Founded and led by codewithumar.

Talk to the studio →
RELATED READING
Product Engineering · 6 min read

Where Shopify stops being customizable

Product Engineering · 7 min read

How to tell a real Next.js development company from a React shop

Product Engineering · 6 min read

Hire a full stack AI developer before you hire an ML team

PRODUCT ENGINEERING

AI-built app breaking in production? Send it over and we'll tell you what it needs.

Send the problem. You get one fixed number and a plan back within a business day.

Duskel
Duskel
AI AUTOMATIONSOFTWARE

We build software worth keeping — for clients, and for ourselves.

Founded & led by codewithumar

© 2026 Duskel. All rights reserved.DUSKEL SMC-Private Limited · Incorporated 2021 · Lahore, PakistanBuilt to last, not to demo.