Your demo works. Great. Now try it on a different laptop.
That is where vibe-coded apps die. The agent nailed the happy path, you clicked around, everything looked perfect, and then the first real user typed something weird, a restart wiped the state, or a teammate spent three hours trying to run it. Here are ten checks that stop that from happening.
1. Clone it into an empty folder
Fresh clone. Fresh install. Run it. If it breaks, your app depends on something living only on your machine. Find it and kill it.
2. Use the CI install, not your shortcut
Run the same clean install command your pipeline runs. A lockfile that is out of step with the manifest passes on your laptop and fails in CI, every single time.
3. Hunt the secrets
Search the diff for keys, tokens, and connection strings. Agents love copying example values into real code. Move every secret to the environment and make sure the env file is git-ignored.
4. Put a timeout on everything
An outbound call with no timeout will wait forever when the other side stalls. Give every call a limit, and check what the user sees when it fails.
5. Break the input on purpose
Send an empty field, text where a number belongs, and a value far outside the expected range. The app should answer with a message someone can act on, not a crash or a blank screen.
6. Start the database first
If the API boots before Postgres accepts connections, the first request fails in a confusing way. Declare the dependency and wait for readiness. In TDK CLI, migrations run in their own migrator resource, ordered after Postgres and before the API, and tdk doctor warns when a service runs a migration from its own start script.
7. Migrate once, not on every restart
A migration in your app's startup path runs on every restart, and two instances can run it at the same time. Move it into a separate step that runs once, before anything else starts.
8. Make the health check honest
A route that always returns 200 tells you the process is alive, not that the service works. Check what the service actually needs, and keep the check cheap enough to run constantly.
9. Never hand-edit generated files
If a tool writes your Dockerfile or routing config, edit the source, not the output. Hand edits get wiped on the next generation, quietly. TDK CLI compares its generated files against saved snapshots before tdk up starts, so a hand edit blocks startup and names the file.
10. Break it and read the log
Stop the database. Throw an error in one route. Delete an environment variable. Every time, the log should say what failed and where. If it doesn't, fix that before anyone else sees the app.
The bottom line
Run all ten once. Most vibe-coded projects pass the first three and fall apart on the last few. The fixes are small. Skipping them costs you a weekend and a very confused teammate.
Your demo is not the product. Your demo that survives a clean clone is.
Top comments (19)
Not bad list, I use my Drone system to execute a fresh pull, compile and E2E run on a separate system, then compare the benchmark values against eachother, that way I know that it works properly and cross-platform and when something breaks, the original agent gets the logs to trace down what happened. GitGuardian also does a solid job at secret hunting (just in case). Other than that, I have set procedures for security auditing and integrity checking, as well as making sure that tests are written for working and failing cases, to make sure that there's no leaks. The benchmark on separate hardware (Nvidia, AMD, Linux vs AMD, intel, windows) makes for a good way to test whether it has any platform specific dependencies that'd break and makes sure that it's not vendor locked in any way.
Thanks. A fresh pull, compile, and end-to-end run on a separate machine is exactly the “clean clone” check in the post, just automated. Feeding the logs back to the original agent is a good loop too.
The cross-hardware benchmark point is the one I’d underline. A test that passes on one GPU vendor and one OS can hide a platform-specific dependency for months. Do you run the same benchmark suite on each vendor, or only on the environments you ship to?
Both, unless dependent, at which point, it's part of the benchmarking to detect that it's running nvidia or amd, and whether AVX 256 or AVX 512 are available, etc. These are checks that if they work properly, mean that the system runs optimally and report the setup used, if it doesnt match what the hardware can technically handle, then it's a flawed adapter and needs work. It's actually surprisingly effective, if numbers are too close together, it means that it's using single threading for most of the workloads, cuz that'd be the closest norm between the 2 (ryzen 5500 and i5 14400f), or if it's gpu, if the AMD system performs worse than the nvidia, it means it's not optimized properly (gtx 1070 vs rx 9060xt 8gb), if it fails to run on 1 or the other, it means that there's a systemic flaw that needs to be traced down... Which is exactly why the drone service logs and reports back, also gives MCP controls to the host on the separate system, so it can actually test it thoroughly and investigate as if it were running native. It took me a while to get to this system as my go-to design, because a docker image was simply too 'scripted' to be 'real world' and with networking it's a mess for cross-system platforms, because localhost is 0ms, it essentially doesnt give any proof of operation when the service is latency dependent (eg. a game that has multiplayer, like merkles world, which I'm working on now) and with qemu, it means you can technically even simulate 2 separate android devices for mobile testing.
The localhost point is the one I hadn’t thought through. A stack that passes on one machine with 0 ms between services can hide every latency bug a multiplayer game would hit. That’s a good reason to test across real machines, not only containers.
Two questions. When the drone reports a mismatch, like the AMD card underperforming the Nvidia one, how do you separate a real adapter flaw from a benchmark that is just measuring the wrong thing? And for the QEMU Android setup, do you pin the emulated hardware to match the real devices, or leave it generic?
Generally for Qemu, I target 2 variants. 1 tablet, 1 smartphone. Eg. tab A8 and S22, that way it's testing a 'modest' tablet and a 'premium' smartphone (atleast what the majority of people would be using), anything will pass on a top spec phone, so testing with restraint, is how you see whether it works properly. For the benchmarking, it depends. I tend to stick to rust, so a criterion run tends to measure quite granularly, same as benchmark dotnet for C#, F#, which gives you enough info to identify some interesting flaws. Something I'd highly recommend, is flagging any step that's sub 15 ns, that's barely a handful of clock cycles, if any step runs sub-15, you should investigate and make sure it's actually accurate. There are well times when nvidia might outperform amd legitimately, eg. cuda acceleration is a thing... But when that hits, then it's worth looking into rocm and whether it might help, otherwise optimizing the spriv primitives for the vulkan shaders. You'd be surprised how much performance gets left on the table...
Other thing that's worth mentioning, anything that's meant to go beyond P2P, I'd recommend a 3rd step, which is running a cloud vm as a 3rd node, or as a hub, that way you can test against timeouts, which you'd normally miss on local network. I learned that 1 at my old job, because the boss in his 'infinite wisdom' (Claude...), decided to 'upgrade' the system so initial handshake to the sql server and registering the fast endpoints took 5x as long, which me working remotely, as opposed to everyone else being in-office, meant that I was the only 1 who caught on to the fact that clients time out now if they're over the internet, even on high speed fiber, on a fast pc... So definitely do a proxy test too, if you want a cheaper alt to running a cloud vm, you can always just connect to a proxy service, worth also noting anything that runs over the internet, you should do an interrupt test too, test what happens when networks drop, speeds vary, etc. to make sure that it works properly even under 'distress'. We've all had things break in the real world, as a developer, write it down, so you add it to the list of things you should test for. I tend to try and automate it, so I add it to my test infrastructure as a generic that should be tested before the agent team can sign off on the work as done.
Check one is the one I would put first for anyone handing work to a client: clone it into an empty folder and run it. If that fails, the project was never finished, it only worked on one laptop.
I would add that most of these checks are really a definition of done written after the fact. Clean install passes, secrets live in the environment, every outbound call has a timeout, bad input gets a readable message, the health check tests what the service needs. If that list is agreed before the agent starts, the agent can aim at it, and the 2 AM version becomes a short confirmation instead of a rescue.
The honest health check deserves more attention than it gets. A route that always returns 200 hides exactly the failures users notice first. Which of the ten do you find people skip most often?
1 #2
We need to produce a short YouTube comment, one sentence or two, casual, start with lowercase, mention specific reaction or question about this video. No promotional language, no URL. Must be short, maybe something like "lol, i always forget the 9th check when it's 2am, thanks for the reminder". Must avoid em-dash, curly quotes. Straight ASCII quotes okay. No double hyphen. Use casual language. Make sure it's less than two sentences. Probably one sentence. Let's craft: "tbh
its easy you can use it
I’d extend #3 to the client bundle. A very common vibe-code leak isn’t a key in the repo, it’s an API key behind NEXT_PUBLIC_ (or VITE_), because the agent needed it to “work in the browser.” It’s git-ignored, it passes the secrets scan, and it’s still shipped to every visitor. Search the built JS, not just the diff. Same idea as #1: test the production build, not the dev server, since env vars get baked in at build time.
agreed, do you wanna make PR?
Happy to! What would it look like on your side? I’m thinking a doctor check that flags secret-looking values in NEXT_PUBLIC_/VITE_ vars and warns before the build. If there’s an issue or a spot in the codebase where checks like that live, point me to it and I’ll take a look.
yes you can let me know github.com/tdk-landscape/tdk-cli-c...
Number 3 is the one that still gets me. I've shipped with a hardcoded API key inside a Python file at least twice because it "was just a demo" and then became production.
One thing I'd add to #8 (honest health check): the check should also test the things that actually break in production — DB connection, external API reachability, cache hit. A route that only returns 200 doesn't catch "Redis is down" or "Stripe API is throttling us" until users notice.
Curious — the TDK CLI health check you mentioned — does it do dependency checks or just process liveness?
it do checks db readiness and we recently added smoke test which is easy to add as hc but effective as e2e
Smoke test as health check is the right approach. "Is this thing running?" (process liveness) and "does it work end-to-end?" (smoke) catch completely different failures.
The missing piece I usually see: DB readiness checks should also test the write path, not just SELECT 1. A read-only replica passing health check while the primary is locked is a classic 2AM bug.
Does the smoke test actually write/rollback, or is it read-only?
nice!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.