Your Vibe-Coded App Works. That's Not the Same as Ready.
It Works on Your Screen
A few years ago, getting from an idea to a working app took weeks of engineering time. Now it takes an afternoon. You describe what you want to Cursor, Lovable, Bolt, v0, or Claude, and a real application appears: login, dashboard, database, payments. You click through it and everything works.
That is a genuine shift, and it is worth celebrating. But “working” describes what happens when you use the app the way you intended. Production is about what happens when thousands of strangers use it in ways you did not intend, some of them on purpose. Those are different tests, and most AI-generated apps only pass the first one.
We review a lot of vibe-coded projects at Code43. The same handful of gaps shows up in almost every one of them, regardless of which tool built it. None of them are exotic. All of them are fixable. The problem is that none of them are visible from the user interface, so they stay hidden until something goes wrong.
Why AI-Built Apps Share the Same Blind Spots
AI coding tools optimize for the request in front of them. When you ask for “a page where users can see their invoices,” the model builds a page that shows invoices. It is not thinking about the user who changes the invoice ID in the URL to see someone else's. You did not ask about that user, so they are not part of the spec.
The tools also take the shortest path to a visible result. Putting an API key directly in the frontend makes the feature work immediately. Turning off database security rules makes the permission errors go away. Each shortcut is reasonable in isolation, and each one leaves a door open.
Finally, a lot of what makes an app production-ready is not code at all. Backups, monitoring, and rate limits are configuration and infrastructure decisions. An AI that only edits files in your repository will not go set up your database backup schedule.
1. Secrets in the Browser
This is the most common problem we find and the most expensive one. Somewhere in the frontend code there is an API key for OpenAI, Stripe, SendGrid, or a similar service. Anything that ships to the browser can be read by anyone who opens DevTools. Automated scrapers look for exactly these keys, and a leaked AI key can run up thousands of dollars in charges before you notice.
Frameworks make this easy to do by accident. In Next.js, any environment variable prefixed with NEXT_PUBLIC_ is baked into the client bundle. In Vite, the prefix is VITE_. When an AI tool hits an “undefined” error because a server variable is not available in the browser, adding that prefix is a common “fix.” It works, and it publishes your key.
The rule is simple: any key that can spend money or read private data belongs on the server. The browser calls your own server route or server action, and the server calls the third-party API with the secret. If a secret has ever been in client code or in a public Git repository, assume it is compromised and rotate it.
Not every key is a secret
Some keys are designed to be public. The Firebase web config and the Supabase anon key are meant to ship to the browser. They identify your project; they do not grant access by themselves. Access is controlled by your database rules, which brings us to the next problem.
2. The Database Is Open to Everyone
Backend-as-a-service platforms like Firebase and Supabase let the browser talk to the database directly. That is what makes them so fast to build with. It also means the database rules are the only thing standing between your data and anyone with your public key.
Firebase security rules
When you create a Firestore database in test mode, the default rules allow anyone to read and write everything for 30 days. When that window expires, the app starts throwing permission errors. The quick fix, which AI tools will happily suggest, is allow read, write: if true. The errors disappear and the database is now permanently public.
Real rules check who is asking. A user should be able to read their own profile document and nobody else's. An admin collection should require an admin claim. Firebase provides a rules simulator in the console to test these before you deploy them.
Supabase row-level security
Supabase uses Postgres row-level security (RLS). With RLS enabled and good policies, the anon key can only see the rows the current user is allowed to see. With RLS disabled on a table, the anon key can read and write every row in it. Tables created through raw SQL or migrations do not always have RLS turned on, and AI-generated migrations frequently skip it. The Supabase dashboard includes a Security Advisor that flags tables without RLS. Check it.
3. The Client Is Not a Security Boundary
Many AI-built apps enforce their rules in the user interface only. The admin button is hidden for regular users, but the admin endpoint behind it does not check who is calling. The signup form validates the email address, but the server accepts whatever it is sent. The checkout page calculates the price, and the server trusts that number.
Anyone can bypass a user interface. They can call your API directly with a tool like curl, edit the request in DevTools, or change an ID in a URL. Every server route and server action needs to answer three questions on its own, without relying on the frontend:
- Who is this? Verify the session or token on the server for every request that touches private data.
- Are they allowed to touch this specific record? Being logged in is not the same as owning invoice #1043. Checking ownership prevents users from reading each other's data by editing an ID.
- Is the input valid? Re-validate lengths, formats, and types on the server. Look up prices from your own records or from Stripe, never from the request body.
4. Nothing Stops Someone From Hitting It a Million Times
An endpoint that works fine for one user can be called ten thousand times a minute by a script. For most routes that is a nuisance. For routes that cost you money, it is a real problem: AI chat endpoints, email sending, SMS verification, and file uploads all bill per use.
Rate limiting caps how often a single user or IP address can call a route. Hosting platforms like Vercel and Cloudflare offer firewall rules, and libraries such as Upstash Ratelimit add per-route limits in a few lines. Public forms need extra protection against bots. We walked through the layered approach we use on our own contact form in Adding AI to Your Website.
It is also worth setting spending caps and usage alerts directly with every paid API provider. If something slips past your limits, a hard cap turns a disaster into an inconvenience.
5. When Something Breaks, You Won't Know
In development, errors show up in your terminal. In production, they happen on someone else's phone and vanish. Without monitoring, you find out about bugs when a customer emails you, or more likely, when they quietly stop using the app.
An error tracking service like Sentry captures exceptions from both the browser and the server, with the stack trace and the context needed to reproduce them. Pair it with basic uptime monitoring that alerts you when the site stops responding. Both take under an hour to set up.
Watch the opposite problem too. AI-generated error handling often sends raw error messages straight to the user. A database error that includes table names or a stack trace tells an attacker a lot about your system. Log the details on the server and show the user a plain, friendly message.
6. No Backups, No Way Back
If your production database were deleted tonight, how much would you lose? For many vibe-coded apps the honest answer is “everything.” A bad migration, an AI agent running a destructive command, or a compromised key with write access can all wipe data in seconds.
Check what your database plan actually includes. Free tiers often include limited backups or none at all, and features like Firestore scheduled backups and point-in-time recovery must be turned on explicitly. Then test a restore once, so you know the process works before you need it.
Backups also argue for a separate staging environment. Letting an AI tool run migrations or experiments directly against the database your customers use is how a small mistake becomes an outage. A second project with test data costs little and gives you somewhere safe to break things. Our guide to hosting and deployment covers how preview and production environments fit together.
Prompting Your AI to Close the Gaps
The same tools that created these gaps can help close them, as long as you ask directly. The model will not volunteer a security review, but it is reasonably good at one when prompted with specific questions.
Ask for an audit before asking for fixes. A prompt like “List every environment variable used in client-side code and tell me which ones are secrets” gives you a concrete list to act on. So does “For every API route and server action, show me where it verifies the user's session and checks that they own the record they are modifying.” Any route without a clear answer needs work.
For the database, paste your Firebase rules or Supabase policies and ask the model to describe, in plain language, what an unauthenticated user can read and write. If the answer surprises you, the rules are wrong.
Verify the result yourself. Open your deployed site, open DevTools, and search the loaded JavaScript for sk_, key, and the names of your API providers. Log in as one test user and try to load another user's records by changing the ID. These checks take minutes and they tell you more than the model's confirmation that everything is secure.
Practical Takeaways
- Keep secrets on the server. Anything prefixed with
NEXT_PUBLIC_orVITE_is public. Rotate any key that has ever shipped to the browser or been committed to a public repository. - Lock down the database. Replace test-mode Firebase rules, enable RLS on every Supabase table, and confirm what an anonymous user can access.
- Check identity and ownership on the server. Hiding a button is not access control. Every route should verify the session and confirm the user owns the record.
- Rate limit anything that costs money. AI, email, SMS, and uploads first. Set spending caps with every paid provider.
- Add error monitoring before launch. Know about failures before your users report them, and keep raw error details out of the browser.
- Turn on backups and test a restore. Use a staging environment for migrations and AI-driven experiments.
Vibe coding is a legitimate way to build software, and the apps it produces are often a strong foundation. What they usually need is a focused engineering pass on the layers nobody sees in a demo. If you have an AI-built app that is about to meet real users and you want a second set of eyes on it, book a production readiness review with Code43.
Security starts at the connection itself. Read how HTTPS protects data in transit, or see how we use a layered AI pipeline to keep spam out of a production form.
Need help with your infrastructure?
Whether it's DNS, deployment, or full-stack architecture — Code43 can help you get it right.
Book a Consultation