Skip to content
QLTech
9 min readBy QLTech

Why vibe-coded apps break in production (and what fixing them actually involves)

AI tools get you to a working demo fast. Here are the specific ways AI-generated apps fail once real users arrive, how to tell whether yours is salvageable, and what a proper rescue looks like.

  • Vibe coding
  • AI app rescue
  • Production readiness

Vibe coding is the practice of building software mainly by prompting an AI tool and reacting to what comes back. You describe a feature to Lovable, Cursor, Claude Code, Bolt.new, v0, Replit, Windsurf or Base44, look at the result, and prompt again until it feels right. The AI is the primary author. You are the editor with a vague brief.

It is genuinely useful. A founder can put a real product in front of real customers in a weekend. A product manager can test an idea without waiting for an engineering sprint. An operations team can build the internal tool nobody would ever have prioritised. We are not here to sneer at any of that. Some of the most interesting projects we see started as a prompt.

The trouble is that vibe coding optimises for the demo. The tools are very good at producing something that works on one machine, with one user, clean inputs and a small dataset. Production is the opposite of every one of those conditions. That is where projects stall, and the ways they stall are remarkably predictable.

The failure modes we see most often

It works locally and nowhere else

The app ran perfectly in the preview. Deployed, it returns 500 errors, an environment variable is missing, the build depends on a package that was installed globally on someone's laptop, and the database URL points at a development instance that no longer exists. There is no documented way to go from a clean checkout to a running system. Often there is no repository at all, just an export.

AI tools get this wrong because "make it work here" is the only instruction they ever received. Nobody asked for a reproducible build, so nobody got one.

Every fix breaks something else

You find a bug and prompt for a fix. The AI rewrites half a file, the bug goes away, and a feature that worked last week quietly stops working. You prompt again. Something else breaks. We call this the vibe cycle, and it is the point at which most people realise the codebase has stopped converging.

It happens because there are no tests to tell the AI what must keep working, and because each prompt only sees a slice of the system. The model is not being careless. It simply has no way of knowing what it is not allowed to change.

Authentication and permissions are held together with hope

This is the one that keeps us up at night. The login page looks fine. Underneath, permission checks run in the browser where anyone can bypass them, the admin dashboard is protected by a hidden link rather than a server-side check, API keys are shipped to the client bundle, and the Supabase or Postgres row-level security that would stop users reading each other's data was never switched on.

None of this shows up in a demo, because in a demo there is only one user and that user is you.

It falls over the moment real users arrive

Ten users are fine. A hundred feel slow. A thousand is an outage. The usual causes are unglamorous: a list page that runs one database query per row, tables with no indexes, no caching anywhere, images served at full resolution, and no rate limiting on the endpoints a bored teenager will find within a day of launch.

Payments, emails and webhooks are flaky

Stripe works in test mode. In live mode the webhook that marks an order as paid was never verified, so it either fails silently or accepts anything. Transactional email goes to spam because the sending domain was never authenticated. The retry logic is a comment that says TODO.

Nobody can explain the code

The person who prompted the app cannot maintain it, and the first developer they hired looked at it for an hour and quoted a full rewrite. The code is not necessarily bad. It is simply undocumented, inconsistent from file to file, and structured according to whatever the model happened to generate on the day.

Is yours salvageable?

Probably, but be honest with yourself about the following.

  • Can someone check the code out and run it from scratch, with written instructions? If not, that is the first job, and it is usually fixable.
  • Do you know where every piece of data lives, and who can read it? If the answer involves the word "probably", assume the worst until proven otherwise.
  • Does the core of the product actually work when you use it like a customer would, on a slow connection, with a second account open in another browser?
  • Are you still able to add features, or has every change become a negotiation with the vibe cycle?
  • Is the stack something a normal engineering team can work with? Next.js, React, Node.js, Python, Supabase, Firebase, Postgres and Stripe are all fine. Something the tool invented is not.

If most of those answers are encouraging, you have a salvage job, not a rebuild.

What a real rescue involves

The pattern that works is the same one we use for any inherited system, just compressed.

Audit first. Someone reads the whole codebase, runs it, probes the security and data layers, and writes down what is broken, what is risky, what is fine and what it will cost to fix. You should get that as a document you could hand to an investor, not a verbal "it's a mess". If the audit is not a fixed price agreed before it starts, ask why.

Stabilise. Critical bugs, security holes and deployment blockers, in that order. The goal of this phase is narrow: a version that real users can safely use. It is not the moment for refactoring things that merely offend the engineer's taste.

Harden. Now the boring work that keeps the app alive: tests around the flows that matter, monitoring and alerts, database constraints and migrations, a repeatable deployment pipeline, cost controls on anything that calls an AI or storage API, and the refactors that make the next change cheaper than the last.

Hand over, or stay on. Documentation, a walkthrough, and repositories and cloud accounts in your name. A good rescue leaves you able to keep using AI tools, because the tests and CI checks now catch the things the tools get wrong. Some clients keep building themselves and bring engineers in for reviews. Others prefer a retainer. Both are fine.

When a rewrite is honestly the right call

Sometimes. If the data model is wrong in a way that touches everything, if the tool generated its own framework nobody else can support, or if the app has grown so tangled that every fix costs more than the feature it protects, a rebuild can be cheaper than a rescue. The audit should say so plainly, with the reasoning, and it should be the exception. Be suspicious of anyone who quotes a rewrite before they have read the code.

Where QLTech fits

We take over AI-built codebases, audit them at a fixed price, fix what is broken and harden what is fragile, then hand back a system your team can own. If you have an app that works in the demo and nowhere else, start with our AI app rescue service or send us a short brief and we will come back with a written scope.