Because it fails in a different way than human code. A tired developer writes code that looks wrong and is wrong. A model writes code that looks completely right and is quietly wrong in one place. Reviewers skim confident-looking code faster, so the one bad line gets less attention, not more.
This is now the most common quality problem we deal with on web projects. Not bad code exactly. Plausible code. Code with correct naming, sensible structure, and a function call to something that does not exist.
We use AI in our own work and we are not interested in pretending otherwise. What has changed is the review step. The generation got fast. The checking has to get more deliberate to compensate, and most teams have not made that adjustment yet.
The signal you normally rely on is gone. In human code, confidence usually tracks competence, and messy code warns you to look harder. Generated code is uniformly fluent regardless of whether it is correct, so surface quality tells you nothing about whether it works.
The second difference is volume. A developer might produce a hundred lines in an afternoon and know every one of them. A model produces four hundred in a minute that nobody has read carefully. Review capacity, not writing capacity, becomes the bottleneck, and teams rarely staff for that.
The third difference is intent. A human wrote each line for a reason and can explain it. Generated code is the statistically likely continuation of a prompt, which means there is sometimes no reason behind a line at all. Asking why is this here is a much more useful review question than it used to be.
Adoption is high and trust is falling. The 2025 Stack Overflow Developer Survey reports that 84% of respondents are using or planning to use AI tools in their development process. In the same survey, 46% actively distrust the accuracy of those tools, against 33% who somewhat or highly trust it.
The direction of travel is the striking part. In a February 2026 post on its own blog, Stack Overflow states that only 29% of 2025 respondents said they trust AI, down 11 percentage points from 2024. Usage went up while confidence went down, which is not a pattern you see often.
The named frustration matches exactly what we see in practice. The Stack Overflow survey reports 66% of developers citing AI solutions that are almost right, but not quite, as their biggest problem, and 45.2% reporting that debugging AI-generated code is more time-consuming. Almost right is the whole issue in three words.
We read this as a maturing market rather than a failing one. Developers used these tools for a year, found the specific shape of their unreliability, and adjusted their expectations. That is healthy. What is not healthy is shipping as though the adjustment never happened.
Check that every function, method, and option it calls actually exists. That is the single highest yield check, and it takes seconds. Generated code invents plausible interfaces confidently, and a call to a method that sounds exactly right but is not real will pass a visual review every time.
Second, check the edge cases the prompt never mentioned. Empty arrays, null values, a network request that fails, a user who is not logged in. Models optimise for the happy path described in the prompt, and silence about failure states is not the same as handling them.
Third, check that it does what you asked rather than something adjacent. Generated code often solves a slightly different problem, particularly when the request was ambiguous. It will be good code for that other problem, which makes it harder to spot, not easier.
None of these are exotic review skills. They are ordinary diligence applied in a specific order, because the failure modes are predictable. That predictability is the good news.
Run it, and check the imports against the real documentation. If the code runs and the tests pass, invented calls surface immediately. If you are reviewing without running, open the actual docs for every unfamiliar method rather than trusting that a confident-sounding name is real.
Package imports deserve particular suspicion. A generated import of a package that does not exist is an obvious error. A generated import of a package that does exist but is abandoned, or is a lookalike of the one you meant, is a supply chain problem hiding inside a convenience.
Our rule is that every new dependency introduced by generated code gets treated as a deliberate decision, with the same scrutiny as one a person proposed. Nobody adds a library to a client project because an autocomplete suggested it. That has to be a choice somebody made and can defend.
The habit here is the same one we apply to written content, which is verifying specifics against real sources rather than accepting fluent output. We described that discipline in our piece on how to fact-check AI-generated content before you publish it, and the logic transfers directly to code.
It can, and the mechanism is straightforward. Models generate what is common in their training data, and a great deal of publicly available code contains outdated or insecure patterns. Common is not the same as correct, and security is an area where the common approach is frequently the old one.
The categories to watch are the boring ones. Input that reaches a database without being handled properly. Secrets written into code rather than into environment variables. Permissive settings chosen because they make the example work. Authentication logic that looks complete and covers only the paths the prompt mentioned.
We do not treat this as a reason to avoid the tools. We treat it as a reason that generated code touching authentication, payments, or user data gets reviewed by a person who understands that area, every time, with no exceptions for small changes. Small changes are where this bites.
Slow down and reconstruct the intent before you judge the implementation. When generated code arrives inside a pull request from someone else, you are reviewing two things at once: whether the code is correct, and whether the person who submitted it understands it. The second question matters more.
The most useful review comment we have found is simply asking what happens when a specific input arrives. If the author can answer, they have read it. If they cannot, the code needs a proper review rather than an approval, and everyone now knows that.
This is not about catching people out. It is about keeping ownership attached to code. Software that nobody in the building understands is a liability regardless of who or what wrote it, and that has been true since long before these tools existed.
As a first pass, yes. As the only pass, no. A model reviewing generated code is genuinely good at spotting mechanical problems, missing error handling, and inconsistencies with the surrounding codebase. It is much weaker at knowing what the code was supposed to accomplish.
The failure mode is agreement. Ask a model whether some code is correct and you will often get a well-argued yes, because agreement is a likely continuation of that conversation. Asking it to find problems, or to argue that the code is wrong, produces far more useful output than asking it to check.
We use automated review to widen coverage and human review to decide. Linters, type checking, and tests catch categories of error cheaply and consistently. Judgement about whether this is the right thing to build stays with people. That division has not changed, and we do not expect it to.
We take a similar approach on the quality assurance side of a build, which we walked through in our article on how we use AI to QA a website before launch.
The generated code most likely to reach production on a website is the code least likely to be reviewed properly. Small scripts, form handlers, tracking snippets, and one-off embeds. Nobody opens a pull request for a script pasted into a page, so nobody reviews it.
Those small pieces have outsized effects. A script that blocks rendering damages Core Web Vitals for every visitor. A generated component that produces div elements instead of buttons breaks keyboard access and screen readers. A snippet that fetches data on the client can hide your content from crawlers that do not run JavaScript.
So the practical rule on our builds is that generated code gets reviewed based on where it runs, not on how long it is. Three lines in the site head deserve more scrutiny than three hundred lines in a build script, because one of them ships to every visitor.
None of this contradicts using the tools heavily. It just means the shortcut is in the writing, not in the checking. We made a related argument about the limits of automation in our piece on whether AI can build your website for you.
Write down a short standard and apply it consistently. Ours is simple: generated code must be run before review, every unfamiliar call gets checked against real documentation, new dependencies are explicit decisions, and anything touching auth, payments, or user data gets a specialist reviewer. That is the whole policy.
Then make review capacity a real part of planning. If your team now produces three times as much code, review time has to grow with it or the quality problem simply moves downstream into support and bug fixing, where it costs considerably more.
If you are shipping a site with a fair amount of generated code in it and you want an outside pass before launch, we are happy to help. Reach out at phoenix.studio and tell us what you are building. We would rather find the almost right line before your visitors do.
Tell us where you want to go. We'll tell you how we'd get you there.