A lot of AI-coded software is quietly rotting in production. Here’s what we’ve learned about why, and what actually fixes it.

Dr Jose Crespo put a number on something a lot of us have felt in our gut for a while now. In AI Is Producing More Garbage Code Than Ever, he lays out the numbers behind the vibe: short-term code churn is up roughly 100% since 2021, developers using AI assistants write about 40% more security vulnerabilities, experienced developers are measurably slower with AI tools on unfamiliar codebases, and at some shops engineers are rejecting around 70% of what the AI suggests outright. The “10x productivity” story that justified an awful lot of AI spend is, on the evidence so far, closer to a rounding error once you net out the cleanup cost.
None of that means AI coding agents are useless. It means a lot of organisations shipped a lot of AI-generated code without the discipline that used to keep human-written code honest, and now they’re sitting on codebases nobody fully trusts. That’s a specific, solvable problem. It’s also, as it happens, the business we’re in.
What Should an Organisation Actually Do About This?
An organisation might be tempted to hope they can get away with sub production-quality code, crossing their fingers that nothing will go too terribly wrong. Hope is not a strategy.
Still others will have lost so much faith in the code their AI tooling has produced, that they’re tempted to scrap the whole thing and start again another way. That can be a fatal error, because the competition doesn’t stand still, while you have another go.
So, what is an organisation to do, when they have lost trust in AI generated code?
The honest answer is: not panic, and not pretend it isn’t happening either. Both reactions are common and both are wrong.
The panic response is to freeze all AI-assisted development, mandate line-by-line human review of everything, and quietly hope the problem goes away. That throws out the genuine productivity the tools do deliver on well-scoped, well-supervised work.
In addition, as you go through the rework, you have no solid, measurable evidence that things are getting better, or getting better fast enough.
The denial response is to keep shipping at the same pace, treat “the build is green” as proof of quality, and assume that whatever’s wrong will surface eventually and get fixed then. It usually does surface — normally at the worst possible time, in production, in the worst possible way, in front of customers.
The useful middle path is to treat this the way you’d treat any other engineering quality problem: measure it, understand its root causes, and apply proportionate, disciplined remediation. That’s a solved problem in software engineering. We have the tried and tested tools and techniques. It just hasn’t been pointed at AI-generated code very often yet.
Why Is So Much AI Code Written So Badly?
Two things are true at once here, and neither one is an excuse for the other.
First: humans write mediocre code on average, and that’s what these models learned from. Look at the training corpus honestly — dominated by “normal GitHub,” inconsistent style, non-trivial defect and vulnerability rates — and the realistic quality of the average public code sample is somewhere around 4–5 out of 10, not 8 or 9. Models trained on that corpus inherit its habits, including the bad ones. Agentic coding doesn’t automatically elevate that baseline; independent comparisons of AI-generated versus human-written changes at scale have found AI-authored pull requests carrying roughly 1.7x more issues on average, with 1.5–2x more security vulnerabilities and substantially worse readability. Separately, one widely cited industry figure puts AI-introduced security vulnerabilities at around 45% of cases when the output isn’t reviewed.
Second: the models themselves are still catching up. Earlier generations of coding models are, in hindsight, simply not that good — and a lot of the AI-generated code sitting in production today was written by those earlier models, in a hurry, without the guardrails anyone’s built since. The tools have improved. The habits organisations built around using them mostly haven’t.
Put those together and you get exactly what Crespo’s data shows: code that runs, passes a superficial check, and then falls over the first time it meets a real edge case, a concurrent user, or an attacker.
Can It Be Fixed? Yes — Here’s How We Found Out
I learned most of this the expensive way, building Confelicity, an independent music platform, largely with AI coding agents. It was a genuinely useful education, because every failure mode you’ll read about below, I watched happen in my own codebase before I had a name for it. The behaviours were surprising, because they were the sorts of things the most junior of programmers would (almost) never dare to turn in as finished work.
The takeaway lesson I learned is that the current AI coding agents aren’t trained to write production-quality code. They’re blind, hence indifferent, to the real world issues that seasoned, defensive programmers learned they needed to pre-empt, long before AI attempted to write any code.
What I want to walk through isn’t a list of AI’s shortcomings for the sake of complaining about them. It’s the specific set of behaviours that, once you know to look for them, tell you exactly what tooling and process you need to build around the agent to get production-quality output the first time — which is the discipline we’ve since refined using Kiro’s spec-driven workflow (steering documents, hooks, and structured specs) as the closed loop.
The agent declares victory early, and writes pretend tests
The single most dangerous habit I saw was an AI coding agent reporting a task complete when it wasn’t. Not lying exactly — more like a student who’s confident they answered the exam question because they wrote something under the right heading.
AI agents flatter to deceive. They often sound enthusiastically sycophantic and are programmed to please their users. That’s why they tell you the code they write is complete, when it isn’t. In the heat of code creation, they desperately don’t want to disappoint or delay. In fact, all they do is defer the disappointment for when the truth comes out.
Typically, tests would exist, would pass, and would test almost nothing meaningful: mocked-out dependencies standing in for the actual logic under test, assertions checking that a function returned a value rather than the right value, or coverage of the happy path only. A green build became a signal you could no longer trust without checking what was actually behind it.
Structural, privacy, and security issues, generated wholesale
Left unsupervised, the agent would happily create coupling between parts of the system that had no business knowing about each other — a password reset flow quietly triggering account cleanup, a data export function processing a payment as a side effect. Multiple layers of the programme calculating important results independently but in contradiction with each other. These aren’t hypothetical; they match a documented failure pattern researchers have started calling “memory-collision cascades,” where compressed representations put unrelated concepts too close together and one quietly wakes the other at runtime.
On top of that, privacy and security shortcuts crept in wherever the spec didn’t explicitly forbid them: logging more than it should, validating less than it should, trusting input it had no reason to trust. Part of the solution is rigorous checking of the code structure and integrity, through knowledge mapping and dependency checking, each and every time the code is changed.
It forgets. Context is everything, and it rots.
Standing instructions — coding conventions, architectural boundaries, “we never do X here” — have a shelf life inside an agent’s working context. Object permanence is not the AI coding agent’s strong suit. They often operate as if they were just born and, frustratingly, easily forget important constraints and instructions.
Say it once at the start of a long session and it’ll be gone by the fiftieth exchange, quietly overwritten by whatever’s more recent and more locally relevant to the immediate task. This isn’t a minor inconvenience; it’s the single biggest reason AI-generated codebases drift out of internal consistency over time, feature by feature, each one built to slightly different unstated rules. You cannot fix this by asking nicely once. You need a mechanism that re-injects the standing rules on a cadence, and — better still — external memory that survives across sessions rather than living only in a context window that’s going to get compacted or forgotten. This is exactly the gap tools like Beads, a git-friendly issue tracker and memory system built specifically for coding agents, exist to close: a durable, versioned record of what’s outstanding and what the rules are, that travels with the codebase instead of evaporating at the end of a session.
Performance and code quality were nobody’s job
Nothing in a typical “build the feature” prompt asks the agent to think about algorithmic complexity, query efficiency, or maintainability. Most of the corpus the AI agent was trained on didn’t consider it explicitly or as a priority. So it doesn’t. It optimises for the thing it’s being measured on — does the feature work, right now, on the input I tried — and everything else is free to degrade. You end up with N+1 queries, unbounded loops, and duplicated logic sprinkled across a dozen files, none of which shows up until the system is under real load. By then, you have a severity 1 issue on your hands.
AI Is Not a Substitute for Knowing How to Run Software Projects
There’s a version of this story that blames the model. It’s more useful to look at the decision-making around the model. Pavel Samsonov made a sharp observation on his newsletter, The Product Picnic: frequently the failure isn’t AI itself, it’s that the organisation was already bad at running software projects, and AI adds all the failure modes of a novel technology on top of the ordinary ones. With so many organisations with no previous history of software development suddenly trusting in vibe coding, this was bound to show up as a problem.
Executives, in particular, have found AI to be a convenient place to locate blame for problems that predate it — the hope that a tool can substitute for product discipline, technical leadership, and honest project governance, none of which AI provides and none of which it was ever going to.
In my opinion, AI could have been equipped to add those additional disciplines to its vibe coding wizardry, but the outputs wouldn’t have been as snazzily swift and so, less dazzling, to people that try vibe coding for the first time.
If your organisation didn’t know how to run a software project well with human engineers, handing the keyboard to an agent doesn’t fix that. It just makes the consequences arrive faster, and in code nobody on the team has fully read (or perhaps knows how to read).
AI-Driven Tooling to the Rescue
Here’s the genuinely encouraging part. Every one of the failure modes above has a countermeasure, and most of those countermeasures can themselves run as automated checks inside the AI workflow, not as a separate, resented, human-only review pass bolted on afterward.
The underlying insight, which Valentina Servile makes well in her ThoughtWorks piece, Should we still design code for humans?, is that good design isn’t only a courtesy to human readers — it’s also what keeps the agent’s own inference reliable. Coding agents were trained on human-authored code and rely on the same legibility signals humans do: sensible names, clear module boundaries, low coupling. Feed an agent a tangled codebase and it burns more tokens, makes more wrong assumptions, and produces worse output on the next change — the technical debt shows up literally on the token bill.
Sadly, remediating technical debt can often be more expensive than writing it badly in the first place and certainly far more expensive than writing clean code at the outset, whether you’re reliant on humans or AI agents to do the remediation.
So the fix isn’t “have a human read every line” — that doesn’t scale and it isn’t where the leverage is. The fix is standard quality metrics, that we’ve used for years, wired into the workflow the same way you’d wire them into a human team’s CI pipeline: linters, complexity and duplication scanners, and architecture-boundary enforcement tools like dependency-cruiser or ArchUnit that fail the build the instant a change violates a structural rule, not months later when someone notices the codebase has drifted. Integration, end-to-end and property-based tests, in addition to unit and acceptance tests. Security hardening and adversarial penetration testing. The usual arsenal of techniques.
Pair that with steering documents that encode your actual conventions, kept current rather than written once and forgotten, and non-bypassable review gates on anything touching payments, auth, or data integrity.
Here I’ll make a claim I think is defensible from what we’ve seen so far: training an AI coding agent to work with precision, to production standard, guided by someone who knows how to guide human engineers to the same standard, is arguably faster and more tractable than training a junior or mid-level human engineer to the same level of discipline. The agent doesn’t get defensive about code review, doesn’t need six months to internalise “why we do it this way here,” and will follow a hook’s verdict without an ego. It just needs someone competent setting the hooks.
We Start With an Assessment
We don’t walk into a codebase nobody trusts and start rewriting. We assess it first, on a fixed-price, two-week engagement. Look, don’t touch. Characterise and measure. Assess the damage.
That structure isn’t arbitrary — it mirrors what actually works in legacy-code remediation more broadly. Stop further undisciplined generation against the codebase. Get an independent audit that establishes ground truth: an architecture map, duplication and complexity scores, security gaps, test coverage, and specifically an inventory of what touches critical paths. “Independent” matters here for trust as much as for accuracy — if the same process that made the mess is the one grading its own homework, nobody believes the grade.
We lean on frameworks like Runloop’s Assessing AI Code Quality: 10 Critical Dimensions for Evaluation to structure that assessment: functional correctness, code quality metrics, efficiency and performance, robustness and security, semantic correctness, adaptability, compilation and execution success rate, and more. Every issue the assessment turns up gets filed as a discrete, tracked item — a bead on the string — rather than a vague verbal warning that evaporates by the next standup. That’s not incidental: given how much AI-generated technical debt comes from context simply rotting away between sessions, the fix has to be a record that persists independently of anyone’s memory, human or artificial.
Patient Remediation, Billed in Time and Tokens
The assessment tells you the shape and scope of the problem. Fixing it is a different, open-ended engagement, and we bill it in time plus tokens rather than a fixed quote, because the honest answer to “how long will this take” is: it depends what we find.
Some individual issues genuinely take thirty minutes or more to fix properly, even with AI assistance doing the mechanical work. Others need a human team labouring for days — untangling a piece of Type III “spaghettification,” where separate parts of the system got wired together by an agent that saw a false pattern connecting them, isn’t something you delegate back to an unsupervised agent. That would just relocate the same failure mode one step downstream.
The remediation itself follows a disciplined, incremental shape rather than a rewrite: pin current behaviour with characterization tests before touching anything, then use a strangler-fig approach — build corrected components alongside the fragile ones, verify each in isolation, migrate traffic slice by slice. A big-bang rewrite is the trap that finishes off whatever stakeholder trust is left if — when — it slips its timeline.
Working Through the Beads, Measurably
This is where trust actually gets rebuilt, and it happens slower than anyone wants: not with a single “we fixed it” announcement, but with a visible trend line. Duplication score down. Test coverage up. Incident rate down. Each bead closed is a small, independently verifiable claim rather than a promise.
It matters that this is measurable, because “measurably improve code quality” and “restore trust in AI-generated code” turn out to be almost the same exercise. Stakeholders who’ve been burned once don’t want reassurance; they want a number that was wrong last month and is less wrong this month, shown to them on a cadence they didn’t have to ask for. Numbers they can believe in.
Later: Minimising Tokens, and Matching Tests to the Change
Once a codebase is back to a state anyone would call production quality, the work doesn’t stop — it changes shape. The next phase is efficiency: working out which categories of change genuinely need a full regression pass and which only need a narrow, targeted test, and trimming the token spend that comes from agents re-reading and re-reasoning about context they didn’t need for the change at hand. That’s a tuning problem, not a rescue problem, and it’s a much nicer problem to have.
We Can Save the Day After All
None of this requires believing AI coding agents are a dead end, and none of it requires pretending the last two years of “garbage code” headlines were exaggerated. Both things are true: the tools are genuinely useful, and an enormous amount of what’s been shipped with them needs a disciplined, measured intervention before anyone should trust it in production.
That intervention is a known kind of work. It’s software engineering — the assessment, the tracked issues, the incremental remediation, the metrics that prove it’s working — applied to a new kind of author. We know how to do that part. That’s the whole point.
