What breaks first in a vibe-coded app

What breaks first in a vibe-coded app

Software built fast with AI assistance demos extremely well. That is not a criticism — it is the honest reason the approach has spread. You can get from an idea to something clickable in an afternoon, and what you get looks finished.

Then it meets real users, and it comes apart in a consistent order. We pick up this kind of codebase regularly, and the pattern repeats closely enough to be worth writing down. The visible layer is usually fine. What is underneath it was never designed.

This is not an argument against building that way. Getting to a working prototype quickly is genuinely valuable. It is an argument for knowing what you are standing on before you put weight on it.

First: authentication and sessions

This is almost always the first thing to go, and it is the worst one to discover late.

What gets generated is a login that works. A user signs in, a token comes back, the app stores it, protected screens open. Every part of that is correct for the happy path and the happy path is the only path that was considered.

What tends to be missing is everything around it. What happens when the token expires mid-session — does it refresh quietly, or does the user get a blank screen? Can the token be revoked, and if someone leaves the company, does anything actually stop working? Is authorisation checked on the server for every request, or does the interface simply not show the button? That last one matters most: hiding a button is not access control, and an app where the restriction lives only in the front end is an app where anyone curious has full access.

We check this before anything else because it is the only category on this list where the damage is not confined to your own product.

Second: the data layer

The schema is the decision hardest to reverse, and generated code tends to produce one that mirrors the first screen somebody described rather than the business underneath it.

The symptoms are recognisable. Relationships that should be constrained are not, so orphaned records accumulate. Things that should be separate tables are columns, or the reverse. There are no indexes, because nothing was slow with fourteen rows. Validation lives in the interface, so anything that writes to the database another way can write nonsense into it. And there is no migration history, which means nobody can safely change the shape of the data now that there is data in it.

Most of the performance problems we are called in to fix on these products are schema problems wearing a different hat. Adding servers does not help.

Third: anything concurrent

Two people doing the same thing at the same moment is the category generated code handles least well, because it is invisible in testing. One person clicking through a flow never produces it.

So you get the booking taken twice, the stock count that drifts, the counter that loses increments, the job that runs on two workers simultaneously and sends the same email twice. None of it appears until there is enough traffic for two requests to overlap, at which point it appears constantly and intermittently, which is the worst combination to debug.

The fix is rarely subtle once found — a transaction, a constraint the database enforces, a lock with a sensible timeout. The difficulty is that these problems are structural rather than local, so they are not fixed by patching the place where the symptom showed up.

Fourth: error handling

Generated code is diligent about catching errors and careless about what happens next.

Typically every risky operation is wrapped, and every catch block does the same thing: logs something and carries on, or shows a generic message. The result is a product that rarely crashes and frequently lies. An operation fails, the user is told it succeeded, and the inconsistency surfaces days later as a support ticket nobody can reproduce.

Worse, failures stop being visible to you. If everything is caught and logged at the same severity, the log is noise, nobody reads it, and you find out about problems from customers.

The question we ask of each catch block is simple: what is the user supposed to do now? If there is no answer, the error is being hidden rather than handled.

Fifth: what happens on a bad connection

Development happens on good wifi. The failure modes of a poor connection are not slow versions of success; they are different events entirely.

A request that times out after the server processed it. A response that arrives after the user navigated away and writes into a screen that no longer exists. A retry that duplicates the thing it was retrying. A form submitted twice because the first attempt gave no feedback. An upload that fails at ninety percent with no way to resume.

These produce the support tickets that cannot be reproduced in the office, and they are the reason an app can be rated badly by users while passing every test you run.

What is usually fine

It is worth being even-handed, because a report that finds only disasters is not a report.

The interface is frequently good. Layouts are sensible, the component structure is often cleaner than hand-written equivalents, and the visual work holds up. Straightforward business logic — the calculations and transformations — is usually correct. Third-party integrations against well-documented services generally work.

In other words the parts with clear precedent tend to be right, and the parts requiring a judgement about this specific system tend to be wrong. That is exactly what you would predict, and it is useful, because it tells you where to look.

What this means if you are standing on one of these

Not that it needs rewriting. A full rewrite is the most expensive option available and it is right far less often than it is proposed. Much of what is there is worth keeping.

It means the five areas above should be examined before you build anything substantial on top, because each one gets more expensive to fix in proportion to how much now depends on it. Authentication is cheap to correct in week one and a serious project in year two.

It also means the next features should be scoped with that in mind. Adding a second user role to a product whose access control lives in the front end is not a feature; it is a feature plus the authorisation work that was skipped.

If you want to know what you have

Send us the repository, or just a URL. We will tell you what we find and what we think it would take, before anyone signs anything.

Related reading

Previous Article
How to audit an AI-generated codebase before you build on it
Previous Article
Next Article
Building an app that keeps working when the connection drops
Next Article

Let’s talk about your project

welcome@ideus.biz