By the SurfaceCheckr team. Every finding maps to a check we run in production - see our methodology.
Vibe coding best practices: shipping fast without shipping holes
The pitch of vibe coding is speed: describe it, the model builds it, you ship. The speed is real and worth keeping. What kills a launch is not the speed, it is the small set of things the model never had a reason to check, because they are not visible until the app is public.
So this is not a list of rules that slow you down. It is the short set of habits that keep the fast path safe, plus the one check to run before you tell anyone the URL. None of them require you to stop vibe coding.
Decide where each key lives before you paste it
The single most common vibe-coded mistake is a secret key in the browser bundle. It happens because "make it work" and "keep it secret" pull in opposite directions, and the model optimizes for the first.
The rule is simple enough to hold in your head. A publishable key is public by design and can ship to the browser: a Stripe publishable key (pk_live_), a Supabase anon key. A secret key never can: a Stripe secret key (sk_live_), a Supabase service_role JWT, an AWS AKIA… key. When a model hands you code with a key in it, ask which kind it is. If a framework prefix like NEXT_PUBLIC_ or VITE_ is on a secret, that secret is about to be inlined into the public bundle. Move it server-side.
Turn on the gate for every table
If your stack lets the browser query the database directly, and Supabase and Firebase both do, then an access rule is the only thing between a stranger and your data. The model writes the table; it rarely writes the rule.
On Supabase, tables created in a migration or the SQL editor do not get row-level security automatically. Enable RLS on every table in an exposed schema, then write the policy that says who may read each row. A public signup form needs insert, not select. On Firebase, start in Locked mode and open only the paths a feature actually needs; never ship the allow read, write: if true ruleset, which Firebase itself warns is not for production. The habit: every time the model adds a table, you add its rule in the same sitting.
Ask the model for the secure version, out loud
Models pick the insecure option often when left to default. Veracode's 2025 study found that across 80 coding tasks on more than 100 models, the insecure path got chosen 45% of the time when the prompt gave no security guidance. The lever you have is the prompt. "Add auth to this route and make sure it checks the user owns the record" gets you different code than "add auth." It will not catch everything, but it moves the default, and it costs you one sentence.
Keep deploy artifacts out of the web root
Generated projects leave scaffolding behind: a .env, a backup.sql from seeding test data, a .git folder. When you deploy the whole directory, those ship too, and bots request them by name within minutes of a site going live. Deploy only your build output, not the working folder. If you are not sure what your host serves, that uncertainty is the thing to resolve, because a public /.env hands over every credential in it at once.
Run one external check before you launch
Here is the habit that ties the rest together. Everything above is a deploy-time property, which means you cannot confirm it from the editor. The only way to know what you actually shipped is to look at the live URL the way a stranger does.
Open your site in an incognito window, watch the network tab, request the files that should not exist, and search the bundle for service_role, sk_live_, and AKIA. A passive external scan does the whole sweep at once, reads only what the site already serves, and grades it A through F without touching your backend or logging in. That is what SurfaceCheckr is for, and it is designed for exactly this moment: the vibe-coded app that works, right before its first real visitor.
The honest boundary is worth stating once. An external scan sees what leaked, not whether your business logic is sound; it will not test authenticated flows or log in. When the worry is deeper than exposure, that is pentest territory. For the holes a fast-shipped app actually leaves open, though, the check is quick and the fix is usually one line. Ship fast. Then spend the two minutes that stop the fast part from becoming the expensive part. The full vibe coding security guide has the detail behind each habit here.
Read next
- Does "vibe coding" leave security holes? (Yes, here's where)Shipping fast without shipping holes
- Vibe coding security: what your AI-built app hands the publicShipping fast without shipping holes
- You shipped your MVP this weekend. What did you leave exposed?Shipping fast without shipping holes
- The website security checklist: what to lock down, in order of what gets hit firstShipping fast without shipping holes
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.
External, read-only scan. We only request public URLs - never log in, never send attack traffic.