From Lovable to production: what a no-code SaaS rebuild actually involves
Lovable, Bolt, Replit and v0 hand you real code, which changes what a rebuild means. Most of these apps do not need rewriting from scratch: the interface usually survives and the data layer usually does not. What transfers, what gets rewritten, the five phases in order, and what each path costs in weeks and money.
Search for how to build a SaaS with Lovable, Replit or v0 and you get tool vendors, affiliate comparisons and reviews of which builder is best this month. That is a fine question for week one. This article is for the search you make six months later, when the app has paying users and somebody has said the word rebuild out loud.
The bill for the next thousand users covers whether to move. This one covers how, specifically for apps made with AI builders, because they are a different case from Bubble or Airtable. You have code. That means a rebuild is usually smaller than people fear, and almost always bigger than the builder's marketing suggests.
What you actually have
Before anyone quotes anything, it helps to know what the builder actually produced. The four I see most often leave you with quite different things:
| Built with | Usual stack | Where the backend lives | How the code gets out |
|---|---|---|---|
| Lovable | React, Vite, TypeScript, Tailwind, shadcn/ui | Supabase: your own project, or Lovable Cloud | Two-way GitHub sync |
| Bolt | React with Vite, or Next.js | Usually Supabase | Download or push to GitHub |
| Replit | Whatever the agent chose, often Express and React | Replit's Postgres and Replit Auth | Git from the workspace |
| v0 | Next.js App Router, shadcn/ui | Often none yet, or Supabase or Neon through an integration | Download, or the Vercel project it deployed |
Builders that keep the backend on their own platform and only let you export the frontend behave like Bubble for the purposes of this article: plan a rebuild of the backend from the live app, and use the exported interface as a head start.
What survives and what does not
Having read a fair number of these codebases, the pattern is consistent enough to put in a table. It is a starting point, not a verdict; the review decides.
| Layer | Usual verdict | Why |
|---|---|---|
| Components and styling | Keep | Tailwind and shadcn/ui are what a developer would have chosen anyway |
| Pages and routing | Keep, tidy | The structure matches the product because users shaped it |
| Database schema | Keep as a starting point | Missing constraints, indexes and a migration history, but the shape is real |
| Auth users | Keep | Supabase holds the hashes, so nobody resets a password |
| Data access from the browser | Replace | Every query and write runs client-side, and RLS is the only thing between users |
| Row level security policies | Rewrite | Usually permissive, inconsistent, or missing on some tables |
| Business rules inside components | Move server-side | A rule enforced in a React effect is a suggestion, not a rule |
| Edge functions | Review line by line | This is where the service role key tends to be used for convenience |
| Billing | Usually rebuild | Access is often granted on the checkout redirect, not on a verified event |
| Background jobs, email, audit log | Build | Usually not there at all |
| Tests | Write | Usually not there at all |
| Keys and secrets | Rotate | They have been pasted into chats, prompts and preview builds |
Read that top to bottom and the shape of the job is clear. What the user sees mostly survives. What the user cannot see mostly does not.
Why the data layer is the part that goes
AI builders favour one architecture because it demos well: the browser talks to the database directly through the Supabase client, with the public anon key. It is fast to generate and every feature works immediately. A typical generated action looks like this:
// Runs in the browser, with the public key every visitor has.
async function approveInvoice(id: string) {
await supabase.from("invoices").update({ status: "approved", approved_by: user.id }).eq("id", id);
await supabase.from("ledger").insert({ invoice_id: id, amount: invoice.total });
}Two problems sit in five lines. First, whether this is safe depends entirely on the RLS policies on both tables, and anyone with the browser console can call the same update with any id and any status, including one the button would never send. Second, the rule that an approval writes a ledger entry lives in the browser. If the tab closes between the two calls, the invoice is approved with no ledger entry, and nothing will ever notice.
// Server side: one transaction, the caller's rights checked, the amount read from the database.
export async function approveInvoice(invoiceId: string) {
const { userId, orgId } = await requireMembership("invoice:approve");
return db.transaction(async (tx) => {
const invoice = await tx.invoices.findForUpdate(orgId, invoiceId);
if (invoice.status !== "submitted") throw new ConflictError("Only submitted invoices can be approved");
await tx.invoices.update(invoice.id, { status: "approved", approvedBy: userId });
await tx.ledger.insert({ orgId, invoiceId: invoice.id, amount: invoice.total });
});
}Moving every write behind a server function like this is most of the work in a hardening project. It is not glamorous and it does not change a single screen, which is exactly why the builder never did it. The same mistakes in tenancy, billing and roles show up in apps built with Claude Code and Cursor too; the SaaS plumbing AI coding tools still get wrong goes through them one by one.
The rebuild, in five phases
Phase 1: take custody
Repository, database, domain, Stripe and keys in your name
Phase 2: baseline
Schema into migrations, CI, types, two seeded tenants, the first failing negative tests
Phase 3: close the doors
RLS rewritten, writes moved server-side, service role key out of anything a user can reach
โณ a leak found here is fixed this week, before anything else
Phase 4: add the missing plumbing
Billing on verified events, jobs, email, audit log, monitoring, a restored backup
Phase 5: move hosting and cut over
Usually no data migration at all, because the database stays where it is
Phase 5 is the big difference from a Bubble rebuild. When the database is already yours, cutover is a deploy, not a weekend.
Phase 1: take custody
Move the GitHub repository, the Supabase project, the domain, the Stripe account and the email sending domain into accounts the business owns. Then rotate every key: the Supabase service role key, Stripe secrets, any third-party API keys. Assume each one has been pasted somewhere it should not have been, because in a builder project it almost certainly has. Finally, stop the builder from pushing straight to the main branch. More on that below.
Phase 2: baseline
Most builder projects changed their schema through the dashboard or through prompts, so there is no migration history and nobody can rebuild the database from scratch. Fix that before touching anything else:
supabase link --project-ref <your-project-ref>
supabase db pull # today's schema becomes the first migration
supabase gen types typescript --linked > src/lib/database.types.tsThen add CI that runs the type check, lint and tests on every push, seed a local database with two tenants, and write the first negative tests: a user in one account trying to read, update and delete another account's records. Expect several to fail. That is the point; they are the work list for phase 3.
Phase 3: close the doors
Rewrite RLS so every table has policies scoped to the user's memberships, and delete any policy that amounts to using (true). Move writes, and any read that crosses tenants or carries business rules, into server functions. Check every edge function for the service role key, which bypasses RLS entirely, and keep it only where the function genuinely needs to act as the system. This phase ends when every negative test passes.
Phase 4: add the missing plumbing
This is the part users never asked for and will notice immediately if it is absent: billing that grants access only on a verified Stripe event, background jobs with retries instead of work done in the browser, transactional email from your own domain, an audit log, error monitoring, and a backup you have actually restored. Each is a known quantity, which is why this phase estimates well.
Phase 5: move hosting and cut over
Because the database and auth users already live in your Supabase project, moving the app is a deploy to your own hosting and a DNS change. No password resets, no data export, no frozen weekend. Moving off Supabase later is a separate decision, and there is rarely a reason to make it during the rebuild.
Harden, partial rebuild or full rebuild
The review at the start of phase 1 puts the app on one of three paths. In my experience the first is more common than founders expect:
| Path | When it fits | Weeks | Hours |
|---|---|---|---|
| Harden | The code is readable and the schema is sound; the problems are access rules and missing plumbing | 3 to 6 | 120 to 240 |
| Partial rebuild | Keep the interface, replace the data layer and business logic behind it | 6 to 10 | 240 to 400 |
| Full rebuild | Tangled code, a schema that fights the product, or a backend you cannot export | 8 to 14 | 320 to 560 |
At the same 2026 rates as the SaaS MVP cost breakdown, hardening comes to roughly $3,000 to $6,000 with a senior developer in South Asia, $5,000 to $17,000 in Eastern Europe, and $10,000 to $48,000 through an agency in North America or Western Europe. A partial rebuild is $6,000 to $10,000, $10,000 to $28,000 and $19,000 to $80,000 respectively. A full rebuild matches the figures in the next thousand users. Anyone quoting a full rebuild before reading the code is quoting the path that pays them most.
Can you keep using the builder afterwards?
Often, yes, and it is worth doing. Founders are fast at interface changes in Lovable or v0, and there is no reason to give that up. What has to change is how those changes arrive. With two-way GitHub sync, the builder and a developer are both committing to the same repository, and the builder does not know which files are now load-bearing.
- The builder works on a branch. Its changes arrive as pull requests, and CI has to pass before anything merges.
- It owns the interface, not the data layer. Server functions, migrations and policies are off limits, and the project instructions say so in plain words.
- It never holds production credentials. In July 2025 Replit's agent deleted a production database during an explicit code freeze, and Replit responded by separating development and production databases. The lesson is not about Replit. Any agent with production access will eventually use it.
- The negative tests are the contract. If a builder change breaks tenant isolation, CI says so before a customer does.
That is harness engineering applied to a founder with a builder instead of a developer with an agent. The principle is identical: the tool can be as fast as it likes, as long as something it cannot argue with checks the result.
What to have ready before you talk to anyone
- 1Access to the repository, the Supabase project or equivalent, the hosting account and the Stripe dashboard.
- 2A list of every integration and automation, including anything outside the builder such as Zapier or Make.
- 3Rough numbers: accounts, active users, rows in the largest table, monthly revenue.
- 4The five things that break or confuse users most often.
- 5Anything a customer or prospect has asked for that you could not provide, such as SSO, audit logs or a security questionnaire.
An AI builder gets you a product that works. Production is the product still working when someone is trying to make it not work.
If your Lovable, Bolt, Replit or v0 app has paying users and you are not sure what is underneath it, I will read the code and the database, run the tenant tests, and come back with a written answer: harden, partially rebuild or fully rebuild, what each costs in weeks, and what to fix first. The review is a fixed fee. Get a no-code rescue assessment.
Questions people ask
Can you build a production SaaS with Lovable?
You can build a working SaaS with Lovable, and because it produces real React, TypeScript and Supabase code, getting to production is usually a hardening job rather than a rewrite. The weak point is the data layer: every query runs in the browser with the public key, RLS is often permissive or missing, and billing is often granted on the checkout redirect. Moving writes into server functions and rewriting RLS is most of the work.
How much does it cost to rebuild a Lovable or Replit app?
At 2026 rates, hardening an AI-built app costs roughly $3,000 to $6,000 with a senior developer in South Asia, $5,000 to $17,000 in Eastern Europe, and $10,000 to $48,000 through a North American or Western European agency. A partial rebuild costs $6,000 to $10,000, $10,000 to $28,000 and $19,000 to $80,000 respectively. Anyone quoting a full rebuild before reading the code is quoting the path that pays them most.
What is wrong with Supabase queries from the browser?
Anyone with the browser console can call the same update with any ID and any status, so safety depends entirely on RLS policies, which AI builders often leave permissive or set to using (true). Business rules split across browser calls also break: if the tab closes between approving an invoice and writing the ledger entry, the data goes inconsistent silently. Move writes into server functions that check membership and run in one transaction.
What should I prepare before asking for a no-code rescue assessment?
Have access to the repository, the Supabase project or equivalent, the hosting account and the Stripe dashboard. List every integration and automation, including Zapier or Make. Gather rough numbers for accounts, active users, rows in the largest table and monthly revenue, the five things that break or confuse users most, and anything customers asked for that you could not provide, such as SSO or audit logs. Get a no-code rescue assessment.
Building something like this?
I design and ship these systems for clients: retrieval over private data, agents that complete real tasks, and the Laravel platforms underneath them.