Why AI-Generated Code Looks Finished Before It Is
The app works. You typed what you wanted, the AI wrote it, and now there is a real product on your screen: pages load, buttons respond, data saves. It looks like the finished software you have used your whole professional life. That resemblance is the trap. Most AI generated code problems do not show up on the screen, because the screen is the one part the tool was optimized to get right.
We see this pattern often in client work. A founder brings in an app built with Claude, Cursor, Lovable, or one of their cousins, and the demo genuinely is impressive. The gap is between what the demo proves and what the founder believes it proves. The demo proves the app can work. It says very little about whether the app keeps working when real users, real data, and the occasional bad actor arrive.
A demo exercises the happy path, and AI tools are built to win the demo
Every piece of software has what developers call a happy path: the sequence where the user does exactly what the designer expected. They fill in every field correctly, click things in order, and use the app one at a time on a fast connection. Demos live entirely on the happy path. That is what a demo is for.
AI coding tools are very good at the happy path, and there is a likely reason why. Much of their tuning rewards output that looks correct to the person watching it run. Code that handles a malformed upload gracefully looks identical, in a demo, to code that crashes on one. A failure that is invisible in a demo cannot be rewarded or penalized by that kind of feedback, so the tools get strong at visible correctness and stay weak at the invisible kind.
None of this means the generated code is junk. Much of it is reasonable, and some of it is better than what a rushed human team would produce. The issue is coverage. A human developer with production experience writes the unhappy paths out of habit, because they have been paged at night by the ones they skipped. The AI writes what the prompt asked for, and nobody’s prompt says “handle the case where two users edit the same record at once.”
Where the gaps hide: load, edge cases, and the first hostile visitor
The gaps cluster in three places, and all three share a property: they only appear after launch.
Load is the first. An app serving one person on a laptop never queues two requests at the same time. The same app serving fifty users hits questions the prototype never faced: database connections running out, slow queries stacking up, two writes landing on the same row together. These are AI prototype limitations in the most literal sense: the prototype was never asked the question, so it never had to answer.
Edge cases are the second. Real users paste in emoji, upload the wrong file type, lose their connection mid-save, and click the submit button twice. Each of these is a small event with a small fix, but an app that has handled none of them fails in small ways constantly. The support burden from a hundred minor failures can cost more than one large one.
The first hostile visitor is the third, and the one worth taking most seriously. Public apps get probed. Automated scanners crawl new sites looking for exposed configuration files, open endpoints, and login forms that accept unlimited guesses. A prototype that has only ever met friendly users has typically never been asked whether it can tell a customer from an attacker. We cover what that probing finds in the security section below, because it deserves more than a paragraph.
Invisible debt, category one: code that works once but cannot be changed safely
Developers use the phrase technical debt for shortcuts that work today and charge interest later. Vibe coding technical debt has a particular flavor, because of how the code gets produced. Each prompt generates a working answer to that prompt. Nobody is maintaining a single coherent design across answers, so the codebase accumulates five ways of doing the same thing, logic copied into multiple places, and components wired together in ways no one chose on purpose.
The app still runs. The debt comes due the first time you want to change it. Say you want to adjust how customer accounts work. In a coherent codebase that logic lives in one place. In an accumulated one it may live in four, and updating three of them introduces a bug in the fourth. Every change starts to feel like a gamble, and the changes you were excited to make in month two quietly stop happening.
This is the AI code quality issue that tends to cost the most over time, and it is invisible in any demo. Two apps can behave identically on screen while one can absorb a year of feature requests and the other cannot survive its first. Which one you have is knowable, but only by reading the code, not by using the app.
Invisible debt, category two: security assumptions that no one made on purpose
In human-built software, security holes are usually mistakes: someone knew the rule and slipped. In AI-built software they are more often omissions. Nobody decided to skip the security work. It simply was never in any prompt, so it is not in the code, and the app looks exactly the same without it.
A few patterns come up again and again when we review these codebases. API keys and passwords pasted directly into source files, because that is the shortest path to making the demo work. Permission checks that exist only in the interface, so the app hides the admin button from regular users but the server still obeys anyone who sends the admin request directly. Input fields passed straight into database queries, which is the classic injection setup. Endpoints that trust whatever the browser claims about who is asking.
What makes these dangerous is the mismatch between visibility and severity. A styling bug is visible and trivial. An exposed key is invisible and can mean someone else running up bills on your accounts or reading your customer data. Automated scanners look specifically for these omissions, and they do not wait for your marketing launch to start. Finding these holes before exposure is a risk-reduction exercise, and it is much cheaper as a review than as an incident.
How to find out what you actually have before real users do
The good news in all of this: every gap described above is findable. None of it requires waiting for a failure. A structured review reads the code the way the demo never does, and it answers the questions that actually matter before launch. Are credentials stored properly? Does every endpoint check permissions on the server? What happens under load, with bad input, during a lost connection? Can this codebase be changed safely, or does it need restructuring first?
The honest outcome varies. Some AI-built apps need a focused week of hardening. Some need a deeper rework of specific areas. Occasionally one is in better shape than its founder feared. What the review replaces is guessing, and guessing is the expensive option, because the gaps do not announce themselves. They wait for the worst possible moment, which is usually the week real users show up.
That is the purpose of our Production Readiness Assessment: a written report that names what is solid, what is risky, and what it takes to ship, before any of it becomes an incident. The demo already proved your idea works. The assessment tells you what stands between that proof and a product you can put in front of customers.


