7 things that break when your vibe-coded app gets real users
Your AI-built app works in the demo. Here's what fails first when real traffic arrives — and the fix for each.
You built the app with Lovable, Replit, Cursor, or Claude Code. It works. You shared the link, people signed up, and then something strange happened on a Tuesday afternoon.
The failures below show up again and again when AI-built apps meet real users. None of them are exotic, and all of them are fixable in an afternoon if you catch them early.
1. Hardcoded URLs and environment values
Agents love to write http://localhost:8000 straight into a fetch call. It works on your machine, it works in the preview, and it breaks the moment your front end and back end live on different domains.
Fix: move every URL, key, and toggle into environment variables, and fail loudly at startup if one is missing.
const apiUrl = process.env.NEXT_PUBLIC_API_URL
if (!apiUrl) throw new Error("NEXT_PUBLIC_API_URL is not set")2. Secrets committed to the repo
API keys in .env files that got committed, service-role keys shipped to the browser, Stripe secret keys in client components. If your repo was ever public, even for an hour, assume those keys are compromised.
Fix: rotate the keys, then move them to your platform's secret manager. Keep anything with "secret" or "service" in its name on the server only.
3. Missing database access rules
This is the big one. In March 2025, a scan of 1,645 Lovable projects found 170 apps with open database endpoints, exposing emails, payment details, and API keys (tracked as CVE-2025-48757). The cause was missing or weak row-level security on Supabase tables.
Fix: turn on row-level security on every table, then write policies that match who should read and write each row. Check that the policies actually block access: log out and try to read another user's data.
4. SQLite (or a single file) as the production database
SQLite is great. It's also a file on one machine. Deploy to two instances, or to a platform that wipes the disk on restart, and your data forks or disappears.
Fix: use managed Postgres for anything with users. Run migrations as a deploy step, not by hand.
5. Database connections run out
Serverless functions and autoscaled containers each open their own connections. Postgres has a hard limit. The first real spike looks fine for ten minutes, then every request fails with "too many connections."
Fix: use a connection pooler (PgBouncer, Supavisor, or RDS Proxy) and set a sensible pool size in your ORM. Alert when connections pass about 70% of the limit, not when they hit 100%.
6. No health check, so nobody knows it's down
Without a health endpoint, your host can't tell a broken deploy from a working one. It keeps routing traffic to a container that returns errors, and you find out from a user's tweet.
Fix: add a /health route that checks the database connection, point your load balancer and uptime monitor at it, and only promote a deploy after it passes.
@app.get("/health")
async def health():
await db.execute("SELECT 1")
return {"status": "ok"}7. The bill nobody is watching
A NAT gateway left running, an oversized database instance, logs retained forever. Flexera's 2026 State of the Cloud report estimates 29% of cloud infrastructure spend is wasted, and that's at companies with FinOps teams.
Fix: set a budget alert on day one, tag your resources, and look at the cost breakdown once a month.
The pattern
Every item here is easy to fix and easy to miss. A coding agent will happily fix any of them if you ask. The hard part is knowing to ask, and noticing when something drifts back.
If you want a second pair of eyes on your repo, book a call. We'll go through this list with you, even if you don't work with us.