We scanned 163 Lovable-built apps. Here's what actually ships.
In February, a single vibe-coded app exposing 18,000 users made the rounds. We wondered whether that was an outlier or a pattern — so instead of arguing about it, we measured it.
Method
We collected 180 public GitHub repositories generated with Lovable, identified by the lovable-tagger dependency — Lovable's own build fingerprint, which makes the corpus precise rather than guessed. Each repo was shallow-cloned and scanned with bowline-check, our open-source static analyser, against seven production concerns: secrets, accounts, data, AI cost controls, payments, store readiness, and behaviour under load. 163 scanned successfully; the rest were empty or deleted.
Three rules we held ourselves to: nothing was executed — static analysis only; no repos are named — aggregate numbers only; and where we found live credentials, the owners got a quiet heads-up instead of a headline.
The correction we made before publishing
Our first pass reported 41% of apps leaking secrets. That number was wrong. Most hits were Supabase anon keys — which are public by design; Row Level Security protects the data, not the key's secrecy. We rewrote the scanner to decode each JWT and flag only service_role (full-admin) keys, then re-ran the entire corpus. The honest number is below. It is smaller, and still bad — and we'd rather publish the correction than pretend we never made the mistake.
Results
Median score: 79/100. Not broken — unfinished. Which is what a prototype is supposed to be; the problem is that these ship as products.
Observability — the universal gap
| No error monitoring of any kind | 159/163 — 98% |
| No health endpoint | 146/163 — 90% |
For 98% of these apps, "the first you hear of an outage is an angry user" isn't a joke — it's the architecture.
Accounts — the store-rejection factory
86 of 163 have no auth at all — fine for a demo, disqualifying for launch. Of the 77 that do:
| No account-deletion path | 65/77 — 84% |
| No password reset | 49/77 — 64% |
| Supabase with no Row Level Security anywhere | 18/163 — 11% |
In-app account deletion has been an App Store requirement since 2022 and a GDPR expectation for longer. 84% of these would be rejected before a human reviewer opens the app.
Secrets — smaller than the panic, worse than acceptable
| Real API keys in public source (OpenAI, Google, Stripe, AWS) | 15/163 — 9% |
Supabase service_role key in source — full DB admin, bypasses RLS | 2/163 — 1.2% |
.env present but not gitignored | 32/163 — 20% |
AI cost — no exceptions found
Of the 20 apps calling LLM APIs directly, 18 had no rate limiting whatsoever, and none had per-user quotas. Small sample — which is why we phrase it as "18 of the 20" and not "90%" — but the direction is unambiguous: one scripted user away from a four-figure overnight bill.
Data & load
| Apps with a database but no migrations | 24/66 — 36% |
| No database indexes declared | 39/163 — 24% |
| Slow work (AI/email/media) inline in request handlers | 35/163 — 21% |
Limitations
Say them before someone else does. Selection bias: people who export to GitHub are probably the more serious Lovable users — the true numbers are likely worse. Static analysis gives floors, not ceilings: a rate limiter could live in an API gateway we can't see; rare at this scale, but any single app can be the exception. Small LLM subsample (n=20), phrased accordingly. And the scanner is open source — anyone can re-run this and check us.
What this is not
Not an argument against Lovable, Bolt or Cursor. They're optimised for the demo, and they're genuinely good at it — the prototype is the achievement, and building one is still the cheapest way to learn what you actually want. This is a measurement of the distance between that achievement and a product. The tools aren't pretending otherwise; the people shipping prototypes as products sometimes are.
Check your own
The scanner runs locally in one command — nothing leaves your machine:
npx bowline-check
We're Bowline — a two-person studio that takes AI prototypes to production, which is our bias and our incentive to have measured this honestly. The full gap framework is at /the-gap.