The companion piece to BookBase. Different platform, different playbook: Lovable lets you export the code, so the rescue is a refactor in place rather than a rebuild from scratch.
InvoiceFlow is a small invoicing SaaS: dashboard, invoices, clients, basic filtering. We built it in Lovable in about an hour, then handed the codebase to ourselves as if a client had just sent it over asking for a production audit.
We worked through the codebase end to end and fixed every issue we found. Ten of them are documented below; they range from "your credentials are one commit away from being public" to "your dashboard chart loops over every invoice twelve times on every render." The after-version is running in Vercel and Supabase. The UI looks nearly identical to the before-version. Everything underneath is different.
This is the companion piece to our BookBase case study, which covers the same pattern on base44. The headline difference: Lovable lets you export your code. That changes the whole playbook. Instead of rebuilding from scratch, we kept the repo and transformed it in place.
Vibe-coded rescues split into two engagement types depending on whether you can export.
If you cannot export (base44, Bubble, some no-code platforms), there is nothing to refactor. The app is a rental. The only rescue path is a rebuild on a stack you actually own. That was BookBase.
If you can export (Lovable, Bolt, Cursor, v0, Replit Agent), you have a Git repo, a package.json, the actual source. The right move is almost always to keep it, audit it, transform it, harden it. That is this one.
For InvoiceFlow, the workflow was: clone the Lovable repo, run our production-ready transformation prompt as a first pass, audit what the AI fixed versus what it missed, then do the real engineering work on top. About a week of senior time, end to end.
A cross-section of the work: the security holes that ruin a week, the architecture choices that compound, and the production gaps that make every other problem invisible.
One careless commit away from your Supabase service role key being public on GitHub. The first thing a motivated attacker does is scan repos for leaked credentials.
Every SELECT relied on Supabase RLS for isolation. RLS is good. A well-designed system enforces tenant boundaries at both layers, query and policy, so a misconfigured policy cannot leak data across tenants.
The default Lovable tsconfig had strict disabled. Half the point of using TypeScript is strict mode. Without it, the compiler shrugs at the bugs it is supposed to catch.
Each one is a silent bet that the shape of some runtime value is what you think it is. That bet loses eventually.
A single thrown exception anywhere in the component tree crashes the entire app. No fallback UI, no recovery path, no monitoring to tell you it happened.
Navigation triggered full page reloads, losing all client-side state. In a single-page app, this is the equivalent of slamming the door every time you walk into a room.
The Send Invoice and Save as Draft buttons stayed active during submission. Five separate buttons had this problem. A user clicking twice could create duplicate invoices.
Negative unit prices, accepted. Missing client, accepted. Due date before issue date, accepted. Quantity of -1,000, accepted, and the total just went negative.
The chart needed monthly totals for the past twelve months, so the code looped through the full invoice list once per month. O(12 × n) where O(n) is correct. Invisible at 50 invoices, very visible at 5,000.
Each new invoice got a number like INV-4955 computed client-side by adding 1 to the last one the user had seen. Two users submitting at the same time get the same number. Concurrent submissions silently collide.
AI is pattern-matching to the simplest working implementation, not the correct one. The work is telling the difference.
Because Lovable exported the code, this was an in-place transformation rather than a rebuild. About a week of senior engineering, distributed across the lanes below.
Every row is a specific, independently verifiable fix. This is the shape of a real production audit output: not "we made it better," but a list you can hand to your lead engineer or your auditor.
Not all of the fixes carry the same weight. The four below are the ones that will hurt you first.
The invoice number race condition is a bug that does not manifest until you have concurrent users, which means your very first real traffic spike is also the first time you see it. Fix: move number generation into a Postgres sequence, serve it via an RPC, return the authoritative number from the database after insertion. One migration, thirty lines, gone forever. The general principle is broader: any identifier that needs to be unique cannot be generated on the client.
The unscoped queries were a defense-in-depth issue. RLS held in practice, but "the app does not currently leak data because one specific layer is configured right" is not a production posture. Every query now filters explicitly, RLS was tightened, and a test verifies that a user cannot retrieve another user’s data even with RLS deliberately weakened.
TypeScript strict mode surfaced two dozen real latent bugs once turned on. Strict mode is the cheapest possible way to surface the places where "almost" is hiding, which is the top complaint about AI-generated code in 2026.
The dashboard chart fix was almost cosmetic at prototype scale. The reason to fix it is not the milliseconds, it is that one O(12 × n) loop in a codebase usually means twenty more places where the same pattern exists. Fixing it is partly about the chart, mostly about retraining the codebase’s habits.
Book a production audit. A senior engineer reads your code, runs the diagnostics, and gives you the honest answer. If there is nothing wrong, we will tell you that. If there is a lot wrong, we will tell you that too, in order of how fast it will bite you.
Book a production audit