The vibe coder pre-launch checklist: what actually breaks in production
A field-tested list of the things that quietly torch your launch day, from missing rate limits to leaked API keys to that one Stripe webhook you forgot to verify. Read before you ship.
- ↳Most vibe-coded apps die in production from three things: leaked keys, no rate limits, and unverified webhooks.
- ↳Cursor and Claude Code will happily ship you a Stripe integration that trusts any POST to /webhook. Check the signature.
- ↳If you don't cap per-user spend on OpenAI, Anthropic, or FAL, one stranger with a script can run up a four-figure bill overnight.
- ↳Backups you have never restored are not backups. Run the restore on a Sunday before launch.
I have lost a weekend to a Stripe webhook that accepted any POST request. I have watched a FAL bill go from $4 to $380 in six hours because nobody put a cap on image generations per user. I have shipped an app where the OpenAI key sat in a NEXT_PUBLIC_ env var, and a kid in Brazil noticed before I did.
This is the checklist I wish I had taped above my monitor the first time I let Cursor write an entire backend for me.
It is aimed at vibe coders. People shipping real apps with Claude Code, Cursor, v0, and a credit card. The agent writes most of the code. You are the adult in the room. These are the adult problems.
Your API keys are probably already leaked
First thing. Open your repo. Search for sk-, pk_live, AIza, xoxb, and eyJ. Then check your .env.example, your committed .env.local, your old Vercel build logs, and the screenshot you posted on Twitter showing off your dashboard.
A few specific things I have seen in the last year:
- An Anthropic key in
NEXT_PUBLIC_ANTHROPIC_KEYbecause the agent wanted to call Claude from the browser. - A Firebase admin service account JSON checked into the repo because Cursor put it next to
firebase.ts. - A Stripe restricted key with
writeon customers, posted in a GitHub issue.
Rotate everything before launch. Not after the first scare. Before. Then put a pre-commit hook in - gitleaks is free and runs in about two seconds.
If a key has to live in the browser, it must be a public/anon key with row-level security or a domain restriction behind it. If you cannot explain in one sentence why a key is safe to expose, it is not safe to expose.
Rate limits are not optional, they are the product
Every endpoint that calls a paid API needs a per-user, per-IP, and global ceiling. I have seen too many launches where a single user can hit /api/generate in a while(true) loop and the only thing stopping them is your monthly OpenAI cap.
For a typical Next.js or Astro app on Cloudflare or Vercel, the minimum I ship with:
// Cloudflare Workers + Upstash example
import { Ratelimit } from "@upstash/ratelimit";
const perUser = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(20, "1 h"),
prefix: "gen:user",
});
const perIp = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(40, "1 h"),
prefix: "gen:ip",
});
Add Cloudflare Turnstile on any unauthenticated form that triggers a paid call. It takes 15 minutes to wire up and stops 95% of the scripted abuse you will otherwise see in week one.
Also: set a hard monthly cap on the provider side. OpenAI lets you set a usage limit. FAL lets you set a budget alert. Set them at 2x your expected spend, not 10x.
Webhooks: the silent killer
If you take payments, you have a webhook. If your agent wrote the webhook handler, there is roughly a 50% chance it does not verify the signature. Cursor and Claude Code both have a habit of writing this:
// what the agent gives you
export async function POST(req: Request) {
const event = await req.json();
if (event.type === "checkout.session.completed") {
await grantAccess(event.data.object.customer_email);
}
return new Response("ok");
}
That endpoint will happily grant lifetime Pro to anyone who can curl your URL. The fix is the boring Stripe-docs version with stripe.webhooks.constructEvent and the raw body. Same story for RevenueCat, Resend, GitHub, and every other webhook provider. Verify the signature or do not ship.
While you are in there: make the handler idempotent. Stripe will retry. If your grantAccess runs twice, does the user get charged twice? Get refunded twice? Email-spammed twice?
The four production problems nobody warns you about
Things that have bitten me on apps like Photo AI Studio and Interior AI Designs, in no particular order:
- Timezones. Your cron runs at “midnight” but your Hetzner box is on UTC and your users are in PST. Your daily digest goes out at 4pm. Pin everything to UTC, format on the client.
- Email deliverability. Resend works great until your domain has no SPF, DKIM, or DMARC and Gmail dumps every magic link into spam. Set all three before launch, not after a user complains.
- Cold starts on serverless. A 4-second cold start on
/api/checkoutis the difference between a sale and a bounce. If you are on Vercel free tier with a Python function, you will feel this. Move hot paths to Workers or keep a warm instance. - The 100MB upload. Someone will try to upload a 4K video to your image endpoint. Set body size limits at the edge. Cloudflare lets you cap it. Do it.
Backups you have never restored are not backups
This one is short. If you have a Postgres on Hetzner or a Firebase project, run a restore on a fresh box at least once before launch. I have seen perfectly configured nightly backups that turned out to be 0-byte files for three weeks because the dump command silently failed.
A backup is a file you have successfully restored. Until then, it is a hope.
Observability before launch, not after the fire
You do not need Datadog. You need to know, at 2am, whether the app is up and whether anyone is throwing 500s.
Minimum kit:
- Sentry (or Highlight, or PostHog error tracking) wired into the client and the server. 15 minutes.
- An uptime check on your homepage and one critical API route. UptimeRobot is free.
- A
#alertschannel in Slack or Discord with Stripe, Sentry, and your uptime monitor posting to it.
If you launch without these, you will find out about your outage from a tweet.
The boring 30-minute pre-flight
The night before launch, do this in order. It takes about half an hour:
- Rotate every API key. Update env vars in prod.
- Run
gitleaks detecton the repo. - Hit your paid endpoints from an incognito browser with no auth. Should fail.
- Trigger your Stripe webhook with the CLI using a bad signature. Should reject.
- Send a test email from prod. Check it lands in Gmail inbox, not spam.
- Restore last night’s DB backup to a scratch instance. Confirm the user count.
- Force a 500 on purpose. Confirm Sentry catches it and pings your channel.
- Try to upload a 200MB file. Should bounce at the edge.
If any of those eight fail, you are not launching tomorrow. You are fixing it tonight.
Most of what I do for clients these days is exactly this: take a half-built app that an agent vibed into existence and get it past these eight items so it can survive a Product Hunt day. If you have something sitting at 80% done and you are nervous about shipping it, come say hi.
Common questions
▸How do I know if my API keys have already leaked?
Run `gitleaks detect` on your repo, then check GitHub's secret scanning alerts (free on public repos and most paid plans). Search your repo history for `sk-`, `pk_live`, and `AIza`. Check old build logs on Vercel or Netlify and any screenshots you posted publicly. If you find one, rotate it and assume the old value is compromised forever, even if you force-push the commit away.
▸What rate limits should I set for an AI app with no real traffic data yet?
Start conservative: 20 requests per user per hour and 40 per IP per hour on any endpoint that costs you money. Add a global daily cap at roughly 5x what you expect your largest legit user to consume. You can always raise limits when a real user complains. You cannot un-charge a $400 FAL bill from a script kiddie.
▸Is Cloudflare Turnstile enough to stop abuse on a signup form?
For unauthenticated endpoints hitting paid APIs, Turnstile blocks the vast majority of scripted abuse and is invisible to most real users. It is not enough on its own. Pair it with per-IP rate limiting and a sane request body size limit. If the endpoint is high-value, also require email verification before the first paid call runs.
▸Do I really need Sentry for a side project launch?
You need something. Sentry's free tier covers most small launches. The alternative is finding out about errors from angry users on Twitter, which is worse for your reputation than a small monthly bill. Wire it into both the client and server, and pipe alerts into a Slack or Discord channel you actually check.
▸What's the most common mistake in agent-written Stripe code?
Not verifying the webhook signature. Cursor and Claude Code often generate a handler that parses `req.json()` directly and grants access based on the event type. Anyone who finds the URL can forge a `checkout.session.completed` event. Always use `stripe.webhooks.constructEvent` with the raw body and your webhook secret, and make the handler idempotent so retries don't double-grant.
- [1]Stripe: Check webhook signaturesstripe.com
- [2]gitleaksgithub.com
- [3]Upstash Ratelimitgithub.com
- [4]Cloudflare Turnstiledevelopers.cloudflare.com
- [5]OpenAI usage limitsplatform.openai.com
Related posts
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.
Pair-build mode: how we work with vibe coders without taking over
Half-built side projects don't need a rescue team. They need a senior engineer on the other end of a Slack DM. Here's how we actually run pair-build engagements.
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.