The 'Almost Right' Problem: Fixing AI Code That Nearly Works
AI code that is almost right is dangerous because it passes a quick look: it compiles, reads well and handles the happy path, then fails on an edge case, a security check or an API that does not exist. The fix is layered checks that catch near-misses automatically, small diffs, and a human review focused on where AI typically slips.
Code that is plainly wrong announces itself. It crashes, the test goes red, and you fix it in minutes. Code that is almost right is quieter. It looks like something a competent colleague wrote, so it gets less scrutiny, and the bug surfaces weeks later in production, where it costs the most to find.
Why is almost-right AI code worse than plainly wrong code?
Because the cost moves from writing to debugging, and debugging someone else's plausible code is slow. This is not a fringe complaint. In the Stack Overflow 2025 Developer Survey, the top frustration with AI tools, cited by 66% of developers, was "AI solutions that are almost right, but not quite". The second, at 45.2%, was that debugging AI-generated code is more time-consuming.
The same survey shows developers are already wary: only 3.1% highly trust the accuracy of AI tools, and when they do not trust an answer, 75.3% said they would still ask a person. Distrust is healthy, but it is not a process. Relying on a feeling of suspicion means near-misses get caught on tired afternoons only some of the time.
What do near-miss bugs in AI code look like?
They fall into a handful of recognisable patterns, and knowing them makes review faster because you know where to look.
| Pattern | What it looks like | What catches it |
|---|---|---|
| Invented APIs and packages | A method or library that does not exist, or a name that is almost right | Type checker, build, lockfile review |
| Plausible but insecure | Works, but skips output encoding, input validation or an auth check | Security scanning, review of risky paths |
| Happy path only | Fine for normal input, breaks on empty, null, duplicate or very large input | Tests with edge cases written first |
| Quiet behaviour change | A refactor that changes an error message, default value or ordering | Existing tests, snapshot tests, diff review |
| Weakened tests | A failing test "fixed" by loosening the assertion or mocking the thing under test | Reviewing test diffs as carefully as code |
| Off-spec | Solves a slightly different problem than the one you asked for | A written spec and a review against it |
Two of these have good research behind them. A USENIX Security 2025 paper on package hallucinations found that the average share of hallucinated packages was at least 5.2% for commercial models and 21.7% for open-source models, with 205,474 unique made-up package names across the samples. The supply-chain risk is real: anyone can register a commonly hallucinated name and publish something malicious under it.
On security, Veracode's 2025 GenAI Code Security research tested more than 100 models on 80 curated tasks and found OWASP Top 10 vulnerabilities introduced in 45% of cases. Models failed to defend against cross-site scripting in 86% of relevant cases and log injection in 88%, and Veracode reported that larger models did not perform significantly better than smaller ones. Code can be functionally correct and still unsafe.
How do you catch near-misses before they reach production?
Stack the checks so each one catches a different kind of near-miss, cheapest first. No single layer is enough, but together they leave the human reviewer with far less to find.
In order, on my client projects:
- A short spec before the code. Two or three sentences of what done means, including the edge cases. Most "off-spec" near-misses are really unclear requests.
- Types and a build. A type checker catches invented methods and wrong shapes before anything runs. For new dependencies, I check the package exists, is the one I meant and is maintained before it goes into the lockfile.
- Tests written first for the risky parts. Writing the failing test before the change, as the Claude Code best practices suggest for bug fixes, means the AI has to satisfy your definition of correct, not its own.
- Automated security and dependency checks. Linters with security rules and dependency scanning catch a share of the patterns Veracode found, without anyone remembering to look.
- A preview deployment. Clicking through the real change finds behaviour bugs that tests did not think to cover.
- A focused human review. By now, the reviewer can spend their attention on what machines miss: authentication, payments, data deletion, migrations and the test diffs themselves.
Which habits stop almost-right code being written in the first place?
Prevention is mostly about giving the model less room to guess.
- Keep tasks small. A small diff is easy to read in full. A large one gets skimmed, and skimming is exactly how near-misses survive. Small, reviewed pull requests are also the first habit in how software teams use AI coding agents well.
- Give context up front. Name the files, the conventions and the helpers to use. Many near-misses are the model reinventing something your codebase already has. I wrote about doing this in an unfamiliar repository in context engineering for client codebases.
- Ask for evidence, not reassurance. "All tests pass" means little on its own. The best practices page recommends asking for the command that ran and its output.
- Use a reviewer that did not write the code. A fresh session or a read-only reviewer subagent checking the diff against the spec is not swayed by the reasoning that produced it. It is the same reason multi-agent systems split work into stages that produce reviewable artifacts.
- Read test changes first. If a test was edited in the same diff that made it pass, find out why before anything else.
How do you review AI-generated code quickly without missing near-misses?
Review in a fixed order, so attention goes where near-misses hide rather than where the code is most interesting. My order on client pull requests is:
- The spec and the evidence first. What was asked, and what command output shows it works. If either is missing, the review stops there.
- Test changes next. New tests should cover the edge cases in the spec; edited tests need a reason.
- Dependencies and configuration. Any new package, environment variable or permission gets checked by hand.
- Risky paths. Authentication, payments, data deletion and migrations get read line by line.
- Everything else last. Naming and structure matter, but they rarely cause incidents.
This order also makes reviews faster, because a change that fails the first two steps goes back to the agent before anyone spends time on the rest.
When should you throw the code away instead of fixing it?
When you have corrected the same thing twice and it is still wrong. Claude Code's documentation is direct about this: after two failed corrections, a third mostly adds to a conversation that already contains two failed attempts, and starting fresh with one better prompt that folds in what you learned "works better almost every time."
I apply the same rule to my own time. If an AI-generated change needs heavy surgery to be right, it is often faster to discard it, tighten the spec and the test, and regenerate. That only feels wasteful if you count the typing; as the research on AI and developer speed shows, typing is not where the time goes. Treating AI output as cheap and disposable is one of the habits I described in what the top 1% of AI users do differently.
Work with me
I build AI and full-stack products with these checks built in, and I help teams set up the tests, previews and review habits that keep near-miss code out of production. See full-stack development and AI integration, or browse my work and book a scoping call.
Frequently asked
Why is AI-generated code often almost right?
Language models generate the most plausible continuation of the code and context they see, which usually covers the common case well. They have no built-in way to know your edge cases, your security requirements or whether an API really exists, unless a check tells them. That produces code that looks right and is only mostly right.
How do I debug AI-generated code faster?
Reproduce the bug with a failing test first, then let the AI fix it against that test rather than describing the symptom. Keep the change small, ask for the command output that shows the test passing, and if two attempts fail, start a fresh session with a sharper prompt instead of correcting further.
Is AI-generated code secure?
Not by default. Veracode's 2025 research found AI models introduced OWASP Top 10 vulnerabilities in 45% of its test cases, and larger models did not do significantly better. Treat AI code like code from a new contributor: scan it, test it and review security-sensitive paths by hand.
What are package hallucinations?
Package hallucinations are dependencies an AI suggests that do not exist. A USENIX Security 2025 study found at least 5.2% of suggested packages were hallucinated for commercial models and 21.7% for open-source models. Attackers can register those names, so verify every new dependency before installing it.
Sources
Related services
Building something like this?
Naman Gundaniya takes on full-stack and AI projects fixed quote after a free 30-minute scoping call, replies within 24 hours.
Get in touch