What actually goes wrong when an app built with Lovable, Cursor, Bolt or Replit goes to production?

The answer

Almost always the same ten: no row-level security, secrets readable in the browser or in git, no check on what a logged-in user may touch, preview-versus-production drift, queries that collapse under load, no error alerts, untested backups, payments wired only to the happy path, code nobody can safely change, and a platform you do not fully own.

By Muhammad Bilal14 min read

The short version

  • Your app is not badly built, it is unfinished — and it is unfinished in a predictable way, because AI coding tools optimise for 'it runs', which is demonstrable, over 'it holds', which is not.
  • Veracode's Spring 2026 update is the whole story in one line: since 2023 the models went from roughly 50% to over 95% on syntax correctness, while the security pass rate stayed at about 55%. They learned to write code and not to write safe code.
  • The first three problems on this list are data-exposure problems and they are the only ones that get worse while you sleep. Everything else can wait a week; those cannot.
  • Most of the ten are cheap to fix and expensive to discover, which is exactly backwards from how founders budget for them.
  • You do not need to fix all ten before launch. You need to know which ones you have, and to have decided — rather than discovered — which you are living with.

If you built something with Lovable, Cursor, Bolt or Replit and it is now in front of real people — or about to be — this is the list of what is probably wrong with it.

Not because you did it badly. Because these ten things are what AI coding tools do not do, and nobody tells you that when the demo works on the first try.

The most useful reframe I can offer before the list: your app is not broken. It is unfinished, in a predictable way. That distinction matters, because "broken" suggests starting over and unfinished suggests a checklist. It is a checklist. Here it is.

Why the same ten things, every time

In March 2026 Veracode published an update to its long-running study of AI-generated code, testing more than 100 large language models across four languages. Forty-five per cent of generated samples failed security testing by introducing an OWASP Top 10 vulnerability. By language the spread was wide — Java 72%, C# 45%, JavaScript 43%, Python 38% — and cross-site scripting failed in 86% of the samples where it was relevant.

But the number that explains everything on this page is the trend. Since 2023, syntax correctness in these models has climbed from roughly 50% to over 95%. Over the same period the security pass rate stayed flat at around 55%.

The models learned to write code. They did not learn to write safe code.

That is not a scandal, it is a consequence of what they were trained to optimise. "It runs" is demonstrable — there is a green tick, a rendered page, a passing preview. "It holds up when a stranger pokes at it" is not demonstrable in the same loop, so it does not get optimised, so it does not get built. You end up with the visible 80% at extraordinary speed and the invisible 20% not at all.

Independent scanning bears it out. Symbiotic Security examined 1,072 confirmed Supabase-backed applications in June 2026, probing each with unauthenticated read-only requests. Sixteen per cent had at least one critical vulnerability and 29% had at least one high. Only 26 of the 1,072 — about 2% — came back completely clean.

So: the ten.

1. The database has no locks on it

This is the big one and it is first for a reason.

Your app talks to its database from the browser using a public key. That key is meant to be public; what stops it reading everything is a set of database rules called row-level security. If those rules were never written, the key is a key to an unlocked door, and anyone who opens developer tools can read every row in every table.

In 2025 this was assigned CVE-2025-48757 after researcher Matt Palmer found Lovable-generated apps shipping with rules that restricted nothing. It was published on 29 May 2025 with a CVSS score of 9.3 — critical, described as allowing "remote unauthenticated attackers to read or write to arbitrary database tables of generated sites." A follow-up scan found 303 endpoints across 170 projects still exposed.

Lovable disputes the CVE, on the grounds that customers are responsible for securing their own application data. That is worth quoting rather than arguing with, because it is the clearest statement of the situation you are in: the platform's position is that this part is your job.

How you know you have it: open your live app, press F12, watch the Network tab while a page loads your data, and look at what comes back. If a response contains rows belonging to other people, or if the same request still returns real data when you are logged out, you have it. That check takes about two minutes and I have written it out step by step in Is my Lovable app secure?

The fix: switch RLS on for every table holding user data, then write policies per table and per role. Switching it on is one click. The policies are the fiddly part, and the classic failure is a policy that queries the table it is protecting — the infinite recursion detected in policy for relation error that half of r/lovable is currently pasting into ChatGPT.

2. Your secrets are somewhere they can be read

There are two keys in a typical Supabase-backed app and they are not equivalent. The anon key ships in the browser by design and grants nothing on its own. The service_role key bypasses row-level security entirely — it is the database password with extra steps.

They get swapped. Not maliciously: an AI assistant hits a stubborn permissions error, tries the other key, the error goes away, and the fix ships. The same pattern puts .env files into git history, where deleting the file later removes nothing — the old commits still hold the value, and scanning public repositories for committed keys is a fully automated industry.

The scale here is not anecdotal. In May 2026, RedAccess scanned roughly 380,000 applications across Lovable, Base44, Replit and Netlify and found about 5,000 leaking sensitive corporate or personal data, with around 40% of those exposing identifiable information. Their root-cause finding is the part to sit with: a default-public pattern. New projects are publicly accessible unless the builder changes it, and nothing in the build flow makes that obvious.

How you know you have it: decode the apikey header your app sends — if it says service_role rather than anon, treat it as an emergency. Then run git log --all --oneline -- .env .env.local .env.production in your repo. Silence is the clean result.

The fix: rotate first, fix second. A key that has been public stays compromised after you patch the code that exposed it, because whoever copied it still has it. Rotate in Supabase and Stripe, move every secret into your host's environment variables, then redeploy and re-check.

3. It checks who you are, not what you may touch

Authentication and authorization sound like the same word and are completely different jobs. Authentication is the login. Authorization is the question that gets asked afterwards, on every single request: is this particular user allowed to touch this particular record?

AI-built apps almost always have the first and almost never have the second, because the first is visible — there is a login screen, it works, you can demo it — and the second is a condition inside code nobody looks at. The result is that changing /invoice/1042 to /invoice/1041 in the address bar shows you somebody else's invoice.

This is not only a "you" problem. In July 2025, Wiz found that Base44 itself had two unauthenticated endpoints that accepted a non-secret app_id — a value publicly visible in application URIs and manifest files — and would create a verified account on a private application with it. Wix disclosed on 9 July, verified a fix on 10 July, resolved on 13 July and went public on 29 July, and found no evidence of exploitation. Wiz described "several enterprise applications" as affected without giving a total, and I am not going to invent one.

How you know you have it: log in as one user, find a URL or API call with an ID in it, change the ID to a record belonging to someone else, and see what happens. If you get data, you have it.

The fix: the ownership check belongs in the database policy, not in the interface. Hiding a button does not stop a request. This is one of the two or three items on this list where I would not recommend doing it alone if your app has more than one user role — the failure mode of a slightly-wrong policy is locking out your real customers.

4. It works in preview and breaks in production

Preview runs against different environment variables, a different domain, often a different database, and — the one that catches almost everyone — a different set of allowed origins and redirect URLs.

The recurring version of this is email. You wire up sending, it works perfectly in preview, and in production nothing arrives, because the sending domain was never verified. Preview quietly permits an unverified domain and production quietly refuses it, and the failure is silent on both sides. Auth redirects behave the same way: the callback URL is registered for the preview host and not the live one, so login works right up until it is real.

How you know you have it: something that worked yesterday in the editor does not work on your domain today, and there is no error message anywhere — just nothing happening.

The fix: enumerate every environment-dependent value — API keys, sending domains, redirect and callback URLs, allowed origins, webhook endpoints, storage buckets — and confirm each one on the production host specifically. It is boring and it is an afternoon, and it removes an entire category of mystery. This is most of what launch and deployment work actually consists of.

5. It falls over when more than a few people use it

Your app was tested by you, with a database holding maybe fifty rows. Nothing was slow, so nothing looked wrong.

Then real data arrives and three patterns bite at once. Lists load every row rather than a page of them. A screen showing twenty items makes twenty-one database calls instead of one, because the AI generated a lookup inside a loop. And nothing is indexed, so every filter is a full scan of a table that used to have fifty rows and now has two hundred thousand.

None of these are visible at small scale. All of them are catastrophic at medium scale, and the failure is not a clean crash — it is the app becoming steadily unusable while looking fine.

How you know you have it: load your heaviest screen with production-sized data and watch the Network tab. Dozens of small requests where you expected one, or a single request taking multiple seconds, is the signal.

The fix: paginate, add indexes to every column you filter or sort by, and collapse the loops into joins. This is usually the cheapest large improvement available — a day of work commonly takes a page from six seconds to under one.

6. When it breaks, nobody tells you

There is no error tracking. There are no alerts. There is no log anybody reads.

So the way you find out that signup has been failing is that a user emails you — or, more often, does not email you and simply leaves. Every AI-built app I have looked at that had been live for more than a month had at least one thing that had been quietly broken for weeks.

How you know you have it: ask yourself what would happen, right now, if payments stopped working at two in the morning. If the answer is "I'd find out in the morning, maybe", you have it.

The fix: this is the highest-value hour on the entire list. Add an error tracker, point it at both server and browser, and set one alert on your most important flow. Free tiers are sufficient for almost every app at this stage.

7. Your backups have never been restored

Most hosted databases do take backups. The question is not whether backups exist — it is whether anyone has ever restored one and watched the app come back up.

Untested backups fail in dull, predictable ways: the retention window is shorter than you assumed, point-in-time recovery was never enabled on your plan, or the database is captured and the uploaded files in storage are not, so you restore the records and every document they reference is gone.

How you know you have it: you have never done a restore. That is the whole test.

The fix: confirm what is actually captured and how far back, turn on point-in-time recovery if your plan offers it, and do one restore into a scratch project. Once. It takes an hour and it converts a hope into a fact.

8. Payments work only when everything goes right

The happy path gets built because the happy path is what you demo: card in, redirect to success, account upgraded.

What does not get built is everything else. The webhook that confirms the payment actually settled — as opposed to the browser merely arriving back on your success page. Handling the same webhook twice without granting the subscription twice. Failed renewals. Refunds. Cancellations. Someone closing the tab during checkout.

The characteristic outcome is not fraud, it is quiet revenue leakage: people paying and not receiving access, or receiving access and not paying, in ones and twos, for months.

How you know you have it: search your code for a webhook handler. If there isn't one, or if it has no idempotency key, you have it.

The fix: treat the webhook as the source of truth for entitlement, never the redirect. Make it idempotent. Then deliberately test a declined card, a duplicate webhook and a cancellation before you need to.

9. Nobody can safely change it

There are no tests, so nothing tells you when a change breaks something two screens away. There is no staging environment, so every change is tested in production by your users. There is often no meaningful version history, so there is no clean way back.

This is the mechanism behind being stuck at 80%. It is not that the remaining features are hard. It is that each new change has a growing chance of breaking something old, so progress slows, then stalls, then reverses. The prompt-fix-prompt loop that seems to be going nowhere is usually this problem wearing a different hat — and each lap of it costs credits.

How you know you have it: you have started avoiding certain parts of the app, or you fix one thing and something unrelated breaks.

The fix: a staging environment first, since it is nearly free and immediately stops the bleeding. Then tests on the three or four flows that would actually cost you money — signup, checkout, the core action — not on everything.

10. You own less of it than you think

Exporting to GitHub gives you the code. It does not necessarily give you the database, the storage buckets, the environment configuration, the edge functions, the auth provider settings or the domain wiring — and if you built on a platform's bundled backend, some of that may not have a straightforward exit at all.

There is also the part that is not in your control at all. Between 3 February and 20 April 2026 — seventy-six days — a backend regression at Lovable undid access protections the company had deliberately built during 2025. Authenticated users could read chat histories and source code from public projects, including hardcoded database credentials. It surfaced when researcher @weezerOSINT demonstrated that five API calls from a free account retrieved another user's full source, credentials and AI conversation history. Valid reports had been sitting in their HackerOne queue since 22 February and had been dismissed, because outdated triage documentation still described that visibility as intended.

To their credit, Lovable fixed it within two hours of public disclosure and the founders published a full incident report. I am not telling this story to argue that Lovable is careless — the opposite, really. I am telling it because it is the clearest illustration of the tenth problem: for seventy-six days, your source code and your credentials were on someone else's platform, subject to someone else's regression, and there was nothing you could have done about it. Portability is not paranoia. It is the only version of that risk you control.

How you know you have it: try to answer this without checking — if the platform disappeared tonight, what exactly would you have, and how long would it take to run?

The fix: get the code into a repository you own, get the database onto infrastructure you control, and document the environment. It does not have to happen this month, but it should be a decision rather than a discovery.

In what order to actually do this

Not top to bottom. Problems one, two and three are data-exposure problems, and they are the only ones on the list that get worse while you sleep — a leak does not need traffic, it needs time. Do those this week regardless of what else is happening.

Problem six is next, because an hour of error tracking makes every other problem on the list visible instead of theoretical. Then four and eight, because they are the ones costing you users and money right now. Five when your numbers start growing. Seven before you have anything you cannot bear to lose. Nine and ten are strategic rather than urgent — but they are what determines whether the next six months are pleasant or awful.

And a genuine caveat: you do not need all ten fixed before you launch. Plenty of good businesses run for a year on a stack that would fail half this list. The difference between those and the ones that fall over is not the number of items fixed — it is that they knew which ones they were living with, and had chosen to.

If you would rather have someone else go through this

If you read that list and recognised more of it than you were comfortable with, that is the normal reaction and not a sign that you have done anything wrong.

I do a Production-Ready Audit for exactly this: every item above checked against your actual app rather than against a description of it, with a written fix list in priority order and honest costs beside each one. From $499, back in five to seven days. If your app comes back clean I will tell you that, and you keep your money.

If a full audit is more than you want right now, send me a screenshot or a description of whatever is worrying you and I will tell you whether it is serious. No charge and no pitch attached — most of the questions people send me turn out to have two-sentence answers, and it seems unreasonable to charge for those.

There is more detail on the rescue work itself on the AI SaaS rescue page, on getting a stalled build actually shipped on the MVP development page, and answers to the questions I get asked most often on the FAQ.

Follow-up questions

What people ask next

Does this mean Lovable, Cursor and Bolt are bad tools?

No, and I use them. They compress the first 80% of a product from months to days, which is a real and enormous thing. The gap is that the last 20% — the security boundary, the failure handling, the operational scaffolding — is invisible work, and invisible work is exactly what a tool driven by 'does this look right?' cannot be expected to produce. The tool did its job. The job just does not end where the demo does.

How many of these ten will my app have?

In my experience most apps that have never been reviewed have six or seven of them, and almost every one has at least the first two. That is not a judgement on you or the tool — it is what happens when nobody was ever asked to do that part, and it is why the list is worth reading even if you are confident about your app.

Which one should I check first?

Row-level security, without question. It is the only item on the list where the damage compounds with time rather than with traffic — data can be read by anyone for as long as the gap is open, whether or not you have users. There is a five-minute browser check for it that needs no tools and no signup.

Can I fix these myself?

Several of them, yes. Rotating keys, switching on row-level security, adding error tracking and turning on point-in-time recovery are all dashboard-level tasks you can do in an afternoon. Writing correct authorization policies for an app with multiple user roles, fixing query patterns that only fail under load, and making payment handling idempotent are the ones where people reliably get stuck, because the failure mode is silent and the feedback loop is slow.

My app already has real users. Is it too late?

No, but the order changes. With live data you rotate credentials before you touch code, you take a verified backup before any migration, and you fix in small reversible steps rather than one large rewrite. It is more careful work, not impossible work. Most rescues I do are on apps that are already live and already earning.

Related reading

Production-Ready Audit

Every table's row-level security reviewed, keys checked and rotated, auth and payments tested — back as a written fix list in priority order.

From $499 · 5–7 days

Muhammad Bilal, Full Stack AI Developer

Muhammad Bilal

Full Stack AI Developer · Faisalabad, Pakistan

I build and rescue production AI SaaS products with Next.js, Supabase, Stripe and Claude. Most of my work is finishing apps that were started with Lovable, Bolt, Cursor or Replit and stalled somewhere between working and shippable.

SF
MS
BK
AS

5.0★ · 100% job success · 35+ projects delivered

All articles · RSS