2026-07-25 · 5 min read

Technical debt is sneaking in through AI-generated code

Earlier this month LeadDev's Bill Doerrfeld pulled together the evidence that the AI coding era has a maintainability problem, and the centerpiece is the kind of dataset that's hard to hand-wave away: GitClear's analysis of 623 million code changes from 2023 to 2026, covering enough commits that the trends stop being debatable and start being weather. Code duplication is up 81%. Refactoring is down 70% against the 2022 baseline. Error masking, which is GitClear's term for code that silently swallows failures instead of surfacing them, is up 47%. And here's the part that should bother anyone who reviews code for a living: not one of those numbers comes from code that failed a build or broke a test. Every single duplicated helper and every swallowed exception compiled, passed its test suite, got a green checkmark in CI, and was approved by a human reviewer who saw nothing wrong with it.

What the numbers actually measure, and why "AI slop" is the wrong frame

These aren't vibes about code quality declining or someone's gut feeling that "AI code feels worse." They're operations counted across real commits in real repositories. Duplication means a new block of code that repeats logic which already exists somewhere else in the repo, and 81% more of it means models are writing fresh implementations of things the codebase already does rather than finding the existing one and reusing it, which makes perfect sense when you remember that the model doesn't browse the repo the way a human developer would before writing a new function. The collapse in refactoring and in "move" operations means code that gets written stays where it landed: nobody goes back to consolidate, nobody merges the three implementations into one, and GitClear's CEO Bill Harding describes the end state with a sentence that will sound familiar to anyone who's inherited a codebase from a team that used AI heavily: "you have five different implementations of the same thing that are similar yet different." And then there's error masking, which is the quiet one, the one that doesn't even look like a problem in the diff. It's wrapping a call in a try-catch that logs nothing and rethrows nothing, which reads as defensive programming right up until you're debugging an incident at 2am and discover that the error you're looking for was caught and silently dropped three layers ago. Harding's explanation for why models produce so much of this is disarmingly blunt: "AI strongly prefers to write code that won't be labeled as a defect." And hiding the error is the shortest path to looking correct.

Bar chart of GitClear's Maintainability Gap research across 623 million code changes, 2023 to 2026: code duplication up 81%, error masking up 47%, within-commit copy/paste up 41%, refactoring down 70% versus 2022, legacy-code maintenance down 74%.

The costs are already measurable downstream, and they show up in exactly the metrics that engineering leadership watches most closely. Google's DORA research found that every 25% increase in AI usage brings roughly 7% more delivery instability, which is the kind of correlation that's easy to dismiss as noise until you notice it in your own deploy frequency. GitClear's own research roundup adds the paradox on top: heavy AI users produce more durable code per hour and also 9x more churn. The code arrives faster, and it also gets rewritten faster, because nobody consolidated it the first time and now three people are maintaining three copies of the same logic without knowing the other two exist.

Why review waves it through, and why that's not really the reviewer's fault

Every gate in the pipeline before the reviewer checks exactly one question: does this code work? The compiler checks it by refusing to compile if it doesn't. The tests check it by going red if the assertions fail. CI checks it by running the tests again in a clean environment. Maintainability is invisible to all of them, because maintainability isn't a property of whether the code runs correctly today; it's a property of what the code does to the repo over the next six months, and no automated gate has ever been able to see that. And it's nearly invisible in the diff view too, because a duplicated helper doesn't look like duplication in the diff, it looks like a tidy new function with a reasonable name and a clear purpose, because the diff shows you what was added and not the three existing implementations elsewhere in the codebase that do the exact same thing. A swallowed exception looks like somebody being careful. A new dependency for something the repo already handles looks like initiative. The only checkpoint in the entire pipeline that can ask "should this code exist in this shape at all, given everything else that's already here?" is the human reading the diff, and that human is reviewing more code than they've ever reviewed before because the models are producing it at a pace that makes the old PR queue look quaint.

This is worth saying without softening it: approving working code is how technical debt gets in. Nobody merges a broken build into main. The 81% duplication surge went through thousands of reviews where the code did exactly what the ticket asked, and the reviewer had no cheap way to see what it was doing to the repo as a whole, because the repo as a whole isn't what the diff shows you.

What this actually looks like as a review habit

The most effective single thing you can do against the duplication trend takes about thirty seconds per new function in the diff: before you approve, search the repo for the key phrase or concept in the function you're looking at. If you find one existing implementation, the review comment is "let's reuse the one in utils/dates.ts." If you find four, the review comment is "we have five of these now, let's consolidate before adding a sixth." Thirty seconds of searching against an 81% duplication increase is probably the best return on time investment available to a code reviewer in 2026, and the reason it works is that the model will never do this search on its own, because the model doesn't know it's duplicating something.

The second habit the numbers argue for is treating swallowed errors as real findings, not style comments. A catch block that logs nothing and rethrows nothing is a bug that hasn't happened yet, it's a production incident hiding behind a pattern that looks responsible, and it deserves a request-changes even when every test is green and the author says "it's just defensive." The 47% growth in error masking exists precisely because this pattern reads as harmless to reviewers, and the moment you start treating it as a finding instead of a preference, you start catching the ones that would have cost you a Saturday morning in the incident channel.

And the third, which is maybe the least intuitive: ask what the diff should have deleted. AI-written changes almost always add and almost never remove, which means old code paths and old implementations accumulate without anyone going back to clean them up. The 74% drop in legacy-code maintenance is a direct measure of that, the tending work that used to happen naturally has to be explicitly requested now, because the model will never volunteer to delete something. "This new function replaces the old path, so remove the old path" is a perfectly legitimate review comment, and it's one that most reviewers don't leave because it feels like scope creep when really it's housekeeping.

The hardest part: rejecting code that works

None of this is about catching bugs in the classic sense, and that's exactly what makes it hard. Saying no to a diff that compiles, passes tests, and satisfies the ticket requirement takes a specific kind of confidence that most reviewers never had to develop, because in the pre-AI era the question was almost always "does this code work" and not "should this code exist in this shape." That confidence atrophies if you never practice using it, and most review workflows give you no opportunity to practice it at all, because there's no feedback when you approve something that works today but rots the codebase tomorrow. On DiffDojo that judgment is part of the curriculum: over-engineering is one of the nine planted-bug categories, some PRs in the library are deliberately working code that still deserves a request-changes, and the grading compares your verdict against a canonical review that explains why "it works" wasn't the right bar for that particular diff. Today's PR is free, no signup. The maintainability numbers say the industry approves too easily, and the fix starts with reviewers who can look at clean, working, well-tested code and still say "no, not like this" when the shape is wrong.

Try a review

Today's PR might deserve an approve, or it might just look like it does.

Free, no signup. You get the canonical review after your verdict.

Review today's PR →
Read next Engineering leaders should check AI-generated code more than ever
← All posts