Every one of these tools shows you a working app in its own preview. That preview is not a web server, and the difference only becomes visible on the day you point your own domain at it.
I wanted to know how often that goes wrong, so I stopped guessing and counted. Three tools, one hundred public repositories each, the same handful of measurements. Everything below can be re-run from the commands at the end.
What breaks on your domain
A Vite single-page app is one HTML file plus a bundle of JavaScript. Routing happens in the browser: you click a link and the app rewrites the address bar without asking the server for anything. That works until someone asks the server directly — by reloading the page, or by opening a link you sent them.
Then the request for /pricing arrives at a host that has no file called pricing, no
rule pointing it at index.html, and no reason to serve the app at all. A plain static
host answers the only way it can.
Lovable projects contain no rewrite rule of any kind — no _redirects, no
vercel.json, no netlify.toml, no nginx.conf, no .htaccess, no Dockerfile.
That number is about the repository, not about a live site — which matters, and I want to be exact about it. If you deploy to a platform that recognises a Vite app, the platform can add the rule for you and nothing breaks. The moment you deploy anywhere that does not — your own server, a plain object bucket, a shared host — there is nothing in the project that would stop the 404.
Of the 23 projects that did have a rule, 14 had it in vercel.json. That file is
written by the platform, not by the person who built the app.
How I measured this
Only the GitHub API: repository search, the git trees API, and reading individual files. Nothing was cloned. No deployment was requested, no live site was touched, and no key found in any repository was used.
The fingerprint for Lovable is reliable: the lovable-tagger package, a
componentTagger() call in vite.config.ts, a non-standard build:dev script, and a
<meta name="author" content="Lovable"> tag in the built HTML. Four query variants
returned 295 unique repositories; I took the first 100 alphabetically, so the sample is
not picked for convenience.
The 404 figure is an inference, and I would rather say so than dress it up. I did not load 100 strangers' sites and refresh them. I read each repository and checked whether it contains anything that would answer a request for an inner route. Seventy-seven contain nothing.
Keys in public repositories
A browser app has to be handed everything it needs before anyone logs in, so some keys are public by design and that is fine. The problem is the second copy: the local environment file, committed to the repository, holding values that were never meant to leave the machine.
Lovable repositories have a committed .env. All 26 of them already had a
.gitignore.
The ignore file was there in every one of the 26 cases. The key had simply been committed before anyone thought to write the rule.
Once a value is in the history, deleting the file changes nothing — the commit is still
public. Only 16 of the 100 have a .env.example, so for the rest, the list of variables
the app needs is written down nowhere and can only be recovered by reading the code.
The tools are not the same
I expected one story across all three, and that is not what the data says. Lovable and Replit both produce Vite single-page apps and fail the same way. v0 produces Next.js, which routes on the server, and this failure essentially does not arise.
| Measured | Lovable | Replit | v0 |
|---|---|---|---|
| No rewrite rule | 77% | 75% of Vite projects | 5% |
| .env committed | 26% | 2% | 0% |
| A source file over 20 KB | 52% | 76% | 49% |
| Any test at all | 30% | 12% | 4% |
No rewrite rule in the repository
For v0 the 5% is a different measurement: next.config was readable in all 100, and
five of them set output: 'export'. That is the only configuration where a Next.js app
inherits the single-page routing problem.
Replit turned out to be the odd one in the other direction. Fifty-two of its 100 projects ship a real backend — an Express server, and 46 use Drizzle — where Lovable and v0 mostly produce a front end that talks to somebody else's API. That changes which failures matter: an app with a server of its own has to survive a reboot, and nothing in these repositories arranges that.
What I got wrong
Three measurements went into an early draft and came out again. They are worth more than the ones that survived, because they show how easily a confident number can mean nothing.
Corrected · 01
A metric meant to count insecure http:// links reported 22%. Every single match was
http://www.w3.org/2000/svg — the SVG namespace, which is required to look exactly
like that. Real insecure links found: zero. Dropped, not rewritten.
Corrected · 02
I had a figure saying 93% of v0 projects have no rewrite rule. True, and meaningless: Next.js routes on the server, so the file is not part of how it works. The number was measuring the absence of something that should be absent.
Corrected · 03
An abandonment figure turned out to be an artefact of GitHub's default result ordering. Same query, different sort — and the median time since the last commit moved from 370 days to 4. The ordering was the finding, not the projects.
Same query, two orderings
search/repositories?q=…in:readme
The default ordering. Six of the hundred had been touched in the last 90 days.
The rule I took from this: any number produced from the first N results of a search describes the search, not the population.
How long these projects live
So I measured it a way that survives that objection: don't sample at all. The search API reports a total count for any filter, so you can ask how many repositories with a given fingerprint were created in a given month, and how many of those have been pushed to recently. That is a census, and sort order cannot touch it.
Of every 100 repositories created in August 2025, three had a commit in the 90 days before measurement — a year on.
Measured independently, on its own population of 9,380, and it lands in the same place.
The whole curve, by creation month: 3–4% of the August 2025 cohort was still being touched a year later, 6–9% of the December cohort, 10% of February's, 21–28% of April's.
That is not a scandal. Most of these repositories are experiments, and an experiment that ends is not a failure. It does mean any claim about abandoned apps has to say which cohort, measured when — and that the people worth talking to are the small share still working on theirs.
What this doesn't show
Limits of the method
- Public repositories only. Serious commercial work is often private, and none of it is in this sample.
- Repository activity is not product activity. A live, paying app can sit on code nobody has touched in a year.
- Database settings are invisible from code. Three quarters of these projects have no migrations at all, so whether a table is protected cannot be answered from the outside — and I did not try to test it on anyone's live app.
- Nothing here was built or run. I read structure; I did not compile a hundred projects or measure how fast they load.
How to repeat this
Four scripts, in order. They need the gh CLI, authenticated. Nothing is cloned and
nothing from the repositories is executed.
# 0 · find the population by its fingerprint
python3 search.py lovable 100 > repos_raw.txt
# 1 · structure, secrets, fallback rules
python3 harvest.py 100
# 2 · layers, file sizes, tests
python3 arch.py
# 3 · lifespan by cohort, whole population
python3 cohortes.py
The scripts are here: github.com/titov-d/afterbuild-scripts.
The per-repository results are not, and that is deliberate. Those scripts produce a
table mapping real repositories to findings like "committed .env", and publishing
it would be publishing a list of live targets. Method and totals are in the
repository; run it and you will have your own copy of the detail in twenty minutes.
That is a different act from handing it to everyone at once.
If your own app is one of the 77, the repair is an afternoon and you do not need me to describe it: the scripts above will tell you where you stand, and so will any AI you ask. If you would rather hand it to someone who has done it a few hundred times, send it over — we look before we quote.