All Insights
Technical Strategy7 min read

Vibe-Coded Isn't Production-Ready: 3 Missing Layers

By Daniel HaynesAugust 2, 2026
Vibe CodingAI-Generated CodeTechnical DebtCode QualityTechnical StrategyLovable

A vibe-coded app is all surface. The screens work, the demo lands, and the founder ships it to real users — on top of a foundation that was never built. Three layers are missing from every vibe-coded codebase that comes across our desk, and all three stay quiet until the day they don't.

"Vibe coding cleanup" became a job title sometime last year, which tells you how many of these apps are now carrying real users. This is what the cleanup consists of, in the order it matters, and what it costs.

First, the qualification, because it matters: the tools aren't the problem. Lovable, Cursor, and v0 are genuinely good at what they do. The problem is shipping their output to paying users as if it were finished. The output is a working prototype. A prototype is missing exactly the parts that don't show up on screen — and those are the parts that decide whether day 30 is quiet or a fire.

The stack you can see, and the one you can't

A production app is a stack. The top layer is the part a founder can see and a demo can show: the UI, the screens, the happy path where the user does the expected thing and everything works. Vibe-coding tools are extraordinary at that layer — they generate the surface faster than a human can type it.

What they don't generate is the three layers underneath, because none of them have a screen to show. There's no button for "tests." No component for "correct auth." No visible difference between an app that handles errors and one that doesn't — until a user hits the error.

The vibe-coded stack: all surface, no foundation Stack diagram of four layers. The top layer — UI, screens, and happy-path flows — is marked shipped. The three layers beneath it — error handling, correct auth, and tests plus CI — are all marked missing. The vibe-coded stack: all surface, no foundation SURFACE · WHAT YOU SEE UI, screens, happy-path flows SHIPPED LAYER 3 Error handling beyond a raw throw MISSING LAYER 2 Auth that's actually correct MISSING FOUNDATION Tests + CI MISSING

Read the diagram top-down and the shape is the whole story: the product is the roof, and there's nothing holding it up. It stands fine on day one because nothing has pushed on it yet. Here's what pushes on it.

The quarter you can see

We published a line-by-line teardown of a real 120-hour MVP — a Flutter app on Supabase with Stripe, three weeks, paying users on day one. Split those hours by whether the work produces something on a screen, and the ratio is stark: the screens are 32 of 120 hours. Everything else is plumbing that a demo never shows and a generator has no reason to write.

On a real 120-hour MVP, the visible screens are only 32 hours Horizontal bar chart of developer hours by category on a real 120-hour MVP build. UI and screens, the only category that produces something visible, is 32 hours. The invisible categories are data model and access rules 30 hours, payments 18, QA and bug fixing 16, auth and session flows 12, and deploy and release 12 — 88 hours total. The screens are 32 of 120 hours developer hours · our published 3-week MVP teardown UI / screens 32h Data model + access rules 30h Payments 18h QA + bug fixing 16h Auth + session flows 12h Deploy + release 12h has a screen · 32h has no screen · 88h

That 32-hour band is the part a vibe-coding tool reliably nails, and it nails it in an afternoon rather than a week. That is a genuine win — it is also the reason the finished product feels closer to done than it is. The 88 hours underneath didn't get cheaper. They got skipped.

1. Tests — there are none

A vibe-coded codebase ships with zero tests. Not a thin suite — none. That's fine at prototype stage and quietly fatal after launch, because it changes how every bug gets found: not by a test run, but by a user.

Every regression is hand-found. You change a component, it silently breaks a flow three screens away, and the first person to notice is a customer who won't email you — they'll just leave. Without a test on the critical paths, you're relying on a human to manually re-click the entire app after every change, and no one does that past week two. The fix isn't 90% coverage. It's one test on each path that touches money or data, plus a CI check that blocks a red build from shipping. That's an afternoon, and it turns "a user found it" into "the build found it."

2. Auth that's actually correct

Generated auth looks right and usually isn't. It renders a login form, it stores a session, the happy path logs you in — and underneath, it's the single fastest path to a CVE in the whole codebase.

The gaps are the ones you can't see from the screen: sessions that don't rotate, no rate limit on the login endpoint, a password reset that trusts a cached token, and access rules that default to allow instead of deny. The moment one user's request can return another user's row, you have a data leak, and it's invisible until someone reports it — or worse, doesn't. Don't hand-roll this and don't trust a generated version of it. Use Clerk or Supabase Auth, and write access rules that deny by default. We treat auth as load-bearing at every price for exactly this reason.

3. Error handling beyond "throw"

The happy path is the only path a vibe-coded app knows. Everything else is a raw throw — and in the browser, an unhandled throw is a white screen the user can't get out of.

Here is that failure, captured rather than described. Same app, same component, same bad payload — an API response that came back without the items array the render maps over. Without a boundary, React tears down the entire tree and the user is left staring at nothing:

A checkout page rendered completely blank after an unhandled TypeError, with the browser console reading: TypeError: Cannot read properties of undefined (reading 'map')
No error boundary: one component throws and the whole page disappears.

Real error handling is the boring layer no demo ever shows: an error boundary so one failed component doesn't blank the whole page, a retry on the network call that flaked, a fallback state that says "this didn't load" instead of dying, and a log line so you hear about it before your users do. The same failure, with that layer in place, costs the user a card instead of the session:

The same checkout page with an error boundary in place: the header and page survive, and a fallback card reads 'This section didn't load' with a Try again button, while the console shows the error was caught and reported
Same throw, wrapped in an error boundary: the page survives, the user gets a retry, and the failure is reported.

Wire up error monitoring alongside it — the free tier of Sentry covers a new app for months — and you turn "a user hit a wall four days ago and you never knew" into an alert you can act on the same hour.

Missing layer What breaks on day 30 The fix
Tests + CI Every regression is found by a user, not a test run. One test per critical path; CI that blocks a red build.
Correct auth Session, role, and rate-limit gaps — the fast path to a CVE. Clerk or Supabase Auth; deny-by-default access rules.
Error handling One unhandled throw becomes a white screen the user can't leave. Error boundaries, retries, and a logged fallback state.

What the cleanup actually costs

The question every founder asks next is the number. A vibe coding cleanup is a bounded piece of work, not a rewrite — retrofitting these three layers onto an app that already works keeps the surface layer intact. Here is the scope we quote for it, in hours, on a single-founder app with one core loop and payments live.

Retrofit scope for a vibe-coded app: about 40 hours Horizontal bar chart of the retrofit scope on a shipped vibe-coded app: tests on money and data paths plus CI 14 hours, replacing generated auth 10 hours, error handling and monitoring 10 hours, and the initial audit and triage 6 hours — about 40 hours total. The retrofit: about 40 hours typical scope · one core loop · payments already live Tests on money/data + CI 14h Replace generated auth 10h Error handling + monitoring 10h Audit + triage 6h Total retrofit ~40h vs ~120h to rebuild from scratch the surface layer survives — that is what makes it a retrofit

Forty hours against the 120 it would take to rebuild. That gap is the entire argument for the vibe-coded starting point: you keep the quarter that got built fast and you buy back the three-quarters that got skipped. The scope only holds if the surface is coherent — if the generated code has three competing state patterns and no data model, the audit says rebuild, and that is a different conversation.

What to do on day 30

None of this means the vibe-coded app was a mistake. It got you to a working product in a fraction of the time and cost — that's real, and it's the right way to find out whether anyone wants the thing. The mistake is treating the prototype as the finished build. When it starts carrying real users and real payments, three things go from optional to load-bearing:

  • Put a test on every path that touches money or data. Not full coverage — just the paths where a silent break costs you a customer or a charge. Add the CI check that stops a red build.
  • Replace generated auth with a real provider. Clerk or Supabase Auth, deny-by-default rules, rate limits on login. It's the cheapest insurance against the most expensive bug class you can ship.
  • Handle the unhappy path. Error boundaries, retries, fallback states, and monitoring so you hear about failures before your users vote with their feet.

Vibe coding cleanup: common questions

What is a vibe coding cleanup specialist?

An engineer who takes an app built with AI coding tools and makes it safe to run on real users — replacing generated auth, adding tests on the paths that touch money and data, and putting real error handling and monitoring underneath the screens. It is a retrofit role, not a rewrite role: the surface the tool produced usually stays.

Is vibe-coded code secure?

The generated screens are rarely the risk. The auth is. Sessions that don't rotate, no rate limit on login, password resets that trust a cached token, and access rules that default to allow instead of deny are the failures we find repeatedly — and every one of them is invisible from the UI. Assume the auth needs replacing until an audit says otherwise.

How much does it cost to clean up a vibe-coded app?

Budget around 40 hours for a single-founder app with one core loop and payments live: roughly 14 hours of tests plus CI, 10 to replace generated auth, 10 for error handling and monitoring, and 6 for the audit that scopes it. That is against roughly 120 hours to rebuild the same product from scratch.

Should I clean it up or rebuild it?

Clean it up if the surface is coherent — one state pattern, a data model you can name, screens that match the product you actually want. Rebuild if the generated code has three competing state patterns, no data model, or a schema that can't express the next feature. The audit answers this in hours, and it is much cheaper to ask at day 30 than at day 300.

Can I keep using Lovable or Cursor afterwards?

Yes, and most founders should. The tools are genuinely fast at the surface layer. What changes after the retrofit is that generated code lands in a codebase with tests, a real auth provider, and CI that blocks a red build — so the next thing the tool writes gets checked before it reaches a user.

If you built v0 with an AI tool and you're about to put real users on it, that's the moment to have someone check the three layers you can't see. That's what a technical audit is — a senior read of the foundation before it has to hold weight. If the audit comes back saying the surface isn't worth keeping, that's a proper MVP build instead, and it's better to learn that at day 30 than day 300. Start from the stack we'd build this on, or send us the repo and we'll tell you what's missing →

Next ArticleHow Much Does It Cost to Build an App in Charlotte, NC?

Ready to Build Your Product?

Let's discuss how I can help you turn your idea into reality.

Get in Touch