Your vibe coded app assumes it never left your machine

The deploy went green, the build logs were clean, and the app came up serving nothing. Not an error page, not a stack trace, just the shell of the thing with the actual content missing, while the exact same commit ran perfectly on my machine thirty seconds earlier. The reason turned out to be four characters in a gitignore file, and I only found it because I had written down, before I shipped, the one question that pointed straight at it.

If your vibe coded app works locally but breaks the moment you deploy it, the honest answer is that the generated code is full of assumptions that only hold on your machine, and the fix is almost never to regenerate the page. It is to name those assumptions before you ship. The code isn't wrong so much as under-specified for anywhere but here: it hardcodes a localhost URL, it reads an env var that happens to exist in your shell, it reads a file from a directory you never committed, it fetches an asset from a path that is public locally and behind auth in production. None of those throw where they were written. All of them break somewhere else. A short pre-deploy spec that lists what the app reads and what it calls catches the whole class at your desk instead of at 2am.

why does it work locally and break the moment I deploy?

Because your machine is quietly providing things the deploy target never promised. Locally you have every env var you ever exported, a filesystem that contains files you never committed, a network where localhost means the thing you are also running, and, usually, no auth layer sitting between the app and its own assets. A real host gives you none of that unless you asked for it. So the code that leans on any of it keeps working right up until it runs somewhere that does not have it.

That is why the failure feels random and is not. The generated code made a reasonable-looking choice that encoded a fact about your environment, and the deploy is the first place that fact stops being true. The tell is that the break is environment-shaped, not logic-shaped: a hard local run is clean and the deploy is broken, or one path works and another returns a redirect, or the app is fine until it needs the one thing that lives outside the repo. When a bug only appears after the environment changes, stop debugging the code and start listing what the code assumed about where it runs.

what are the assumptions that only hold on my machine?

There are a handful that come up on almost every first real deploy, and they are worth knowing by name because each one is invisible until the environment changes under it. Hardcoded localhost or 127.0.0.1 URLs that resolve to the dev server and to nothing in production. Env vars the code reads with no fallback, present in your shell and absent in the host until you add them there on purpose. Files the app reads at runtime that were gitignored because they looked like local scratch, so the deploy, which ships only committed files, arrives without them. And public requests, a manifest, an icon, a service worker, an image, that pass through auth middleware whose route matcher was scoped a little too wide, so in production they get a login redirect instead of the file.

The pattern under all four is the same. Each is a dependency on something your machine has and the deploy does not, and each one fails silently rather than loudly, because a missing file, a redirect, or an undefined env var is a valid state the runtime knows how to limp past. That is what makes them dangerous. A crash you can chase. A blank that comes from a boundary answering differently instead of erroring is a thing you have to already be looking for.

how do I catch this at spec time instead of at 2am?

Write the environment contract down before you deploy, as three plain questions, and answer each one against the deploy target rather than your laptop. What does this app read at runtime, and is every one of those files actually committed? What URLs and domains does it hardcode, and do they exist in production? What non-page requests have to stay public, and does anything in front of them let them through? It takes ten minutes and it is the difference between a checklist item and a production mystery.

Here is the moment that taught me to run it. The app read its content from a directory at runtime, and that directory had landed in gitignore early, back when it held throwaway local files. By the time it held real content, nothing flagged that the deploy, which ships only what is committed, would arrive without it. Locally the folder was full and everything worked. On the host it would have been empty, and the app would have served exactly the nothing I saw. The pre-deploy question, what does this read at runtime and is it committed, put the gitignored directory on the table before I shipped, not after. The auth-middleware allowlist was the same lesson a second time: the list of paths that must stay public is a contract you write on purpose, not one you reconstruct one grey icon at a time after deploy. Same class, same fix, caught the same way.

where this discipline comes from

The rule is spec before generate, and it reaches well beyond deploys. Naming what a thing must do, and what it depends on, before you build it is what makes the build checkable. A vibe coder shipping fast is not the one who regenerates the page fastest when it breaks. It is the one who spent ten minutes writing down what the code assumes about its environment, so the assumptions are a list they can verify instead of surprises they discover in production. The spec is the persistent memory that governs the build, and any real AI orchestration runs on the same move: the model will happily write code that only holds where it was written, and the contract you set around it is what makes that safe to ship. Spec-first is not ceremony; it is the cheapest place to catch the bug, because at spec time the bug is still a question you can answer instead of an outage you have to survive.

questions that keep coming up

Can't I just check the deploy after it goes live? You can, and you will still be debugging in the worst possible conditions: production is down or wrong, the pressure is high, and the failures are silent, so you cannot even tell how many there are. Checking after is how a ten-minute list becomes a multi-hour hunt. The point of the pre-deploy spec is to move the same check to the one moment when fixing it is free.

Isn't a pre-deploy checklist overkill for a small app? The smaller the app, the shorter the checklist, and the three questions do not change. A one-page site still reads assets, still hardcodes at least one URL, and still has paths that must stay public. Overkill is the four-hour production hunt the checklist would have prevented, not the ten minutes it takes to run it.

How do I know which files the app reads at runtime? Grep the code for the read calls, then cross-check each path against what is committed. Anything read from disk that is not in the repo is a deploy break waiting to happen, whether it is a content directory, a config file, or a data fixture. The reads you find are exactly the first line of the spec.

If you are shipping a vibe coded app that has to survive its first real deploy and you want a sparring partner to write the environment contract with you before it breaks, /work-with-us. Send me what the app reads and what it calls, and we will turn the deploy from a coin flip into a checklist you can trust. Work with VibeKoded.

// part of the ai websites topic

// grab the free starter kit that makes your AI stop forgetting and stop guessing: get it →

// building with AI? the field manual has the structured lessons.

// hitting this on a real build? this is what I fix →