Your vibe-coded app works. Here is what breaks when real users arrive.
The short answer
AI coding tools produce working demos, not production systems. The same seven problems appear in almost every generated codebase: authorization enforced in the browser, secrets in the client bundle, a schema with no constraints, no migrations, no backups, unbounded queries, and no deployment path. None of them show up in a demo. All of them show up with real users.
Why the demo never reveals the problem
A demo is one person, clicking the happy path, on a fast connection, with ten rows of data they created themselves. Every failure mode below needs at least one of the four things a demo does not have: a second concurrent user, an adversarial user, real data volume, or a second deployment.
This is not a criticism of the tools. Lovable, Bolt, Replit, v0 and Cursor are genuinely good at producing a working artefact fast, and a founder who validates demand with one is doing something smarter than a founder who spends six months on architecture nobody wanted. The problem is that the artefact and a production system look identical from the outside, so nothing warns you when you cross from one to the other.
The crossing point is your first real user. Everything below is what we find on the other side of it.
1. Authorization that only exists in the browser
The most common and most expensive failure. The generated app hides the admin button when the logged-in user is not an admin, and stops there. The API endpoint behind that button checks nothing. Anyone who opens the network tab, copies the request and changes one field in it can do whatever the admin can do.
Hiding a control is a user-interface decision. Refusing a request is a security decision. AI tools reliably produce the first and frequently skip the second, because from inside a single-user demo they are indistinguishable.
The fix is not a patch. Authorization has to be enforced in one place on the server, on every request, derived from the session rather than from anything the client sent. Retrofitting that means touching every endpoint, which is why it is the first thing we do in a rescue and why doing it later costs several times more.
2. Secrets that shipped to the browser
API keys for payment providers, email services, storage buckets and AI models end up in client-side code because that is where the generated call was made from. Anyone can read them. Bundlers do not hide them; view-source and a search for the provider prefix finds them in seconds.
Two things follow. First, the key has to be rotated, because it must be assumed compromised from the moment it shipped. Second, the call has to move to a server route, which usually means restructuring the feature that made it rather than moving one line.
The bill for this one is not the engineering time. It is the provider usage somebody else runs up on your account before you notice.
3. A database schema with no constraints
Generated schemas tend to be a set of tables with the right column names and almost none of the rules that keep data honest: no foreign keys, no unique constraints, no not-null where it matters, no check constraints, and no indexes beyond primary keys.
For the first month this is invisible. Then you find two accounts with the same email address, an order pointing at a customer who no longer exists, a null in a field the code assumes is always present, and a listing page that takes nine seconds because there is no index on the column it filters by.
Every one of those is a data-correctness bug that the database itself could have refused to allow. Once bad rows exist, fixing the schema means cleaning the data first, which is why this gets worse every week you leave it.
4. No migrations, so the schema exists in exactly one place
Ask how your database gets from its current shape to its next shape and the answer is usually that someone edits it in a web console. That means there is no record of what changed, no way to reproduce the database on another machine, and no way to reverse a change that went wrong.
It also means you cannot have a staging environment, because a staging environment is by definition a second copy of your schema. Which leads directly to the next problem.
5. One environment, which is production
Generated projects deploy to a single place. There is no staging, so every change is tested by your customers. There is no rollback, so a bad change is fixed by making another change under pressure. There is often no version history outside the tool that generated it.
The cost here is not a single incident. It is that the fear of breaking production makes you stop shipping, which is the thing the AI tool was supposed to have made easy.
6. No backups, or backups nobody has restored
Most founders assume their database host backs things up. Many hosts do, on a schedule and a retention window you have not read. Almost nobody has tried a restore.
An untested backup is a belief, not a backup. The question worth answering today is not whether backups exist but how many minutes of data you would lose and how many hours the restore would take, and whether you have ever proved either number.
7. Queries with no ceiling
Generated code fetches everything and filters in the application: every order, every message, every row, then a slice for the page you are showing. With fifty rows it is instant. With fifty thousand it takes the server down, and it takes it down for every user at once rather than just the one who triggered it.
The same pattern appears in exports, dashboards and search. The fix is pagination, indexes, and moving the filter into the database — routine work, but work that touches every list in the product.
What this costs to put right
Roughly, and assuming the product itself is worth keeping, which it usually is. These are engineering-effort bands rather than quotes; the variable is how many features exist, not how complicated any one of them is.
| Problem | Typical effort | What it costs to delay |
|---|---|---|
| Server-side authorization | 1–2 weeks | A data breach involving your customers' records |
| Rotating and moving secrets | 2–4 days | Provider bills run up on your account |
| Schema constraints and indexes | 1–2 weeks | Grows weekly as bad rows accumulate |
| Migrations and staging | 3–5 days | Blocks every other fix on this list |
| Backups, proven by restore | 1–2 days | Total loss, once |
| Pagination and query limits | 3–5 days | Outages that scale with your success |
Doing them in that order matters. Authorization first, because everything else is repairable and a breach is not. Migrations and staging early, because they are what make the remaining work safe to do on a live product.
What we do not recommend
Starting over. The instinct when an engineer sees generated code is to rewrite it, and it is usually wrong. The product logic encodes decisions you made while talking to real users, and the interface has been validated by people using it. Throwing that away to gain a codebase an engineer finds tidier is an expensive aesthetic preference.
What actually needs replacing is the layer underneath: authorization, schema, deployment, backups. That work can be done while the product stays live, one piece at a time, with your users noticing nothing. A rewrite means months with no new features and a real chance the second version never ships.
We recommend a full rewrite in perhaps one case in ten, and when we do, we show the specific evidence before anyone commits to it.
Where to start this week
- 01Run the network-tab test in section one. It takes five minutes and it is the finding that cannot wait.
- 02Search your frontend bundle for your provider key prefixes. If you find one, rotate it today, before you fix anything.
- 03Ask your database host what your backup retention is, then restore one into a scratch database and time it.
- 04Open the slowest list in your product and check whether it has a limit clause. Then check whether the column it sorts by has an index.
- 05Write down how you would undo your last deployment. If there is no answer, that is the fourth thing to fix.
That list is deliberately something you can do without hiring anyone. If it turns up more than you want to handle, an assessment is the cheapest next step: a few days of reading, running and attacking the codebase, and a written report you own and can hand to any developer, including one who is not us.
