The vibe coder pre-launch checklist: what actually breaks in production
You shipped it in a weekend with Cursor. Now it has users. Here's the unglamorous list of things that break when AI-generated code meets real traffic, real money, and real people.
You spent a weekend with Cursor and Claude Code. You have a working app. People are signing up. Congratulations — you are now in the most dangerous phase of the project.
This is the part nobody warns vibe coders about. The LLM is excellent at getting you to 80%. Production is the other 80%. Below is the checklist I run before letting anything I’ve built with AI tools touch real users. It comes from shipping things that broke, not from a Twitter thread.
The stuff that breaks first
1. Your API keys are in the client bundle
This happens constantly. The AI helpfully wires up an OpenAI call from a React component because that’s what the prompt asked for. It works. It ships. Someone opens DevTools and finds your key.
Go open your built JS bundle right now and grep for sk-, xoxb-, AIza, and your Supabase service role key. If anything hits, you have until someone notices.
# from your build output directory
grep -r "sk-\|service_role\|AIza" ./dist ./build ./.next/static 2>/dev/null
Fix: move every third-party call behind a server route. Yes, even the “just a demo” one.
2. No rate limits on anything that costs money
If your app calls an LLM, generates images, or sends email — and there is no rate limit per user, per IP, and per global budget — you are one bored teenager away from a five-figure bill.
Minimum viable protection:
- Per-user limit (e.g. 20 generations / hour)
- Per-IP limit for anonymous routes
- A hard daily spend cap that kills the route when hit
- An alert at 50% of that cap
Upstash, Vercel’s rate limiter, or a dumb Redis counter all work. The point is having one.
3. The database has no indexes
The AI wrote your schema. It probably did not add indexes beyond primary keys. Everything is fast at 100 rows. At 100,000 rows your dashboard takes 12 seconds to load and your Postgres CPU is pinned.
Before launch:
- Run
EXPLAIN ANALYZEon every query that hits a table you expect to grow - Add indexes on every foreign key and every column you filter or sort on
- Turn on slow query logging
4. No idempotency on writes
Double-clicks happen. Network retries happen. Webhooks fire twice. If your “create order” endpoint can create two orders, it will.
Every mutation that matters needs either:
- An idempotency key the client generates and the server respects, or
- A unique constraint that makes the duplicate impossible at the database level
Stripe-style Idempotency-Key headers are not overkill for a side project. They are the difference between charging someone once and explaining to them why you charged them twice.
The stuff that breaks second
5. Auth edge cases
The AI implemented sign-up. Did it implement?
- Email verification before letting users do anything destructive
- Password reset that actually invalidates old sessions
- Session expiry that isn’t 30 days by default
- Rate limiting on login attempts
- A way to delete an account (GDPR, PIPEDA, App Store policy — pick your reason)
If you’re using Clerk, Supabase Auth, or Auth.js, most of this is handled but not all of it. Read the defaults.
6. Webhooks with no signature verification
Stripe, GitHub, Resend, Clerk — they all sign their webhooks. If your handler just trusts the body, anyone can POST to that URL and trigger whatever logic sits behind it. “Mark subscription as active” is a popular target.
// the line that matters
const event = stripe.webhooks.constructEvent(body, signature, secret);
If that line isn’t there, fix it before launch.
7. No error tracking
You will not know things are broken. Users do not file bug reports — they leave. Sentry has a free tier. PostHog has error tracking now. Pick one and install it. The five minutes you spend on this saves you the week where you don’t realize your signup flow has been 500ing on Safari for three days.
8. Your env vars are not actually set in production
The number of times I’ve seen a feature work locally and silently no-op in production because OPENAI_API_KEY wasn’t set in Vercel — too many. The AI cannot check this for you.
Fix: a startup check that throws loudly if required env vars are missing.
const required = ['OPENAI_API_KEY', 'DATABASE_URL', 'STRIPE_SECRET_KEY'];
for (const key of required) {
if (!process.env[key]) throw new Error(`Missing env: ${key}`);
}
The stuff that breaks third
9. Streaming responses with no timeout
LLM streams hang. They hang on the provider side, on Cloudflare, on mobile networks. If you don’t set a timeout, your serverless function runs until the platform kills it (and bills you). Set a 60-second hard ceiling on every AI call and handle the abort gracefully.
10. Costs you cannot see
You need three numbers before launch:
- Cost per active user per month
- Cost per signup (including free users)
- The single most expensive operation in your app
If you don’t know these, you don’t know whether your pricing works. The AI will not figure this out for you. Open a spreadsheet.
11. Backups
When was the last time you tested restoring from a backup?
If the answer is “never,” you do not have backups. You have hopes. Most managed Postgres providers (Supabase, Neon, Railway) do daily snapshots, but the recovery process is something you want to have practiced once before you need it at 2am.
12. Legal pages that aren’t lies
If you collect email, you need a privacy policy. If you take money, you need terms. The AI-generated boilerplate is fine as a starting point, but read it — it often references jurisdictions, payment processors, or data practices that don’t match what you actually do.
The honest part
I don’t always run this whole list. For a throwaway demo, I run items 1, 2, and 6. For anything with payments or user data, I run all of it.
The thing AI tools change about shipping is the cost of building, not the cost of operating. Operating a production app is the same job it was five years ago: someone has to care when it breaks at 3am, someone has to read the logs, someone has to notice the bill went up 4x.
If you’re a vibe coder shipping fast and you’ve hit the point where the app matters — has paying users, has your name on it, has real data inside it — that’s the point where a second set of eyes is worth more than another feature.
That’s what we do at EdsDev. We take things built with Cursor and Claude Code and get them to the point where they don’t wake you up. Sometimes that’s a two-day audit. Sometimes it’s a longer engagement. Either way, the checklist above is roughly where we start.
Got something running on vibes and prayers? Let’s talk.
Related posts
MVP software development when the prototype already exists
A field guide to finishing what Cursor, v0, and Claude Code started. How to take a half-working vibe-coded prototype and ship it as a real MVP without rewriting from scratch.
Pair-build mode: how we work with vibe coders without taking over their project
Most agencies eat the codebase. We don't. Here's how EdsDev runs pair-build engagements with vibe coders shipping in Cursor and Claude Code — without hijacking the keyboard or the vision.
AI chatbots for customer service: what actually works after 90 days in production
Ninety days of running customer service bots in production. What survived, what got ripped out, and the design choices that decide whether your bot helps or annoys.