The gap nobody warns you about
There is a moment in every vibe-coded project where the app works. You click through it and it does the thing. It feels finished, and the feeling is not irrational. You have watched every feature succeed with your own eyes.
But you have been running a very particular experiment. One user, who is you. One path, which you designed. One session, which is already authenticated. One network, which is your wifi. Zero failures, because nothing has been asked to fail. The demo is a proof that the happy path exists. Production is everything else, and everything else is most of it.
Stubs that grew up and got a job
To make the UI look right while building, the model generates convincing placeholder content. This is genuinely helpful and you should keep doing it. The problem is that nothing in the toolchain remembers it is fake. To the code, a hardcoded array of testimonials and a real database query are both just data arriving on a page.
// TODO: replace with real data
const stats = { users: 10432, revenue: 89200, growth: 24 };
const testimonials = [
{ name: "Sarah Chen", role: "CTO at Acme", quote: "Changed how we work." },
{ name: "Marcus Webb", role: "Founder", quote: "Couldn't run without it." },
];This ships. It ships all the time. On launch day it tells your first genuine visitor that you have 10,000 users, which is the exact moment they decide you are not to be trusted.
Go hunting for these specifically. They cluster in predictable places:
- Dashboards and stats bars. Any impressive number you did not compute from a real query.
- Testimonials, logos, and avatars. Fabricated social proof is the fastest trust-killer on the internet, worse than having none at all.
- Empty states. Often the last thing written and the first thing a real user sees, because on day one every state is empty. Your app’s first impression is the screen you spent the least time on.
- Buttons that go nowhere. Settings, Export, Invite teammates. Rendered, styled, clickable, wired to nothing.
grep -rn "TODO\|FIXME\|coming soon\|placeholder\|lorem\|dummy\|mock" \
--include="*.tsx" --include="*.ts" .
# and the one that finds fake numbers:
grep -rn "10,000\|10432\|John Doe\|Jane Smith\|example.com" .Do this before every launch. It is the cheapest five minutes in this manual.
The promise gap: what your landing page says vs what your app does
This one is structural, and it is the reason this product exists at all. You wrote the landing page early, when you were excited, describing the app you were going to build. Then you built. The plan drifted, as plans do. Features got cut, renamed, postponed.
Nothing anywhere reconciles those two documents. No compiler checks your marketing copy against your routes. Your landing page is, effectively, an unverified claim about software that has since changed without telling it.
So do the reconciliation by hand, at least once. Sit down with your live site and write two columns:
| The page promises | The app actually… | Verdict |
|---|---|---|
| “Export your data to PDF” | Button opens a “coming soon” modal | Cut the claim, or build the feature |
| “Real-time collaboration” | Polls every 30 seconds | Reword it honestly |
| “Bank-level encryption” | Uses HTTPS, like every website | Delete. This one is embarrassing. |
| “Free forever plan” | Hits a paywall at 3 items | Say what the limit is |
Every mismatch is a refund, a bad review, or, at a certain scale and for certain claims, a legal problem. It is also the easiest thing in the world to fix before anyone notices, and impossible to fix gracefully afterwards.
The sad path has never been walked
In development, every request succeeds. It is localhost, it takes four milliseconds, and the database is on your laptop. So the branch where things go wrong is code that has literally never executed, which means it usually does not exist.
Deliberately break your own app. All of these take under a minute:
- Throttle the network. DevTools, Network, Slow 3G. Now click everything. Is there a loading state, or does the button just sit there while the user clicks it four more times and submits four orders?
- Go offline mid-action. Start a save, kill the network. Do you get an error, or an eternal spinner?
- Submit the form twice, fast. Double-charged? Two rows?
- Paste an emoji, a 10,000-character string, and an apostrophe into every input. The apostrophe finds SQL problems. The emoji finds encoding problems. The long string finds everything else.
- Hit Back after submitting. Then Forward. Then refresh.
Be user zero, properly
You have never actually experienced your own app. You have a populated account, a live session, and months of context. Your first real user has none of that.
So do this, exactly, and do not shortcut it: open a private window. Sign up with a genuinely new email. Do not skip a step because you know what it does. Go all the way through to the core action.
You will find, almost guaranteed:
- A verification email that lands in spam, or never arrives at all
- An empty dashboard with no explanation of what to do next
- A flow that assumes data you only have because you seeded it by hand
- An onboarding step that errors because it has never run on a fresh account
Then do it again on your phone, on cellular data. That is where the layout breaks and the 4MB hero image finally announces itself.
The unglamorous things that only matter later
- Backups. Turn them on. Then restore one, on purpose, to a scratch project. An untested backup is a hope, not a backup.
- Error tracking. Sentry or similar, ten minutes to install. Without it, production failures are invisible: users do not report bugs, they leave.
- Rate limits and spend caps on anything that costs money per call. See failure #6.
- A way to contact you. The single cheapest safety net there is. Someone will find a bug you never will, and right now they have no way to tell you.