Skip to content

Technical Debt From AI-Generated Code

5 min read · updated August 3, 2026

Two things plausibly get worse, and both follow from mechanisms rather than from the code being bad: duplication, because the model can only see locally, and rework, because code accepted with less understanding is rewritten sooner.

Why duplication is structural

To call an existing helper, you must know it exists. That requires the helper to be in the context — or for the model to search for it, which costs a turn and which nothing in the prompt asked it to do. To write the logic inline, it needs nothing at all. One of those paths is strictly cheaper and it is the one that produces a copy.

The same asymmetry runs the other way for extraction. Noticing that three call sites now share logic, and unifying them, requires seeing all three. That is a global operation, and a model working from a window sees one. So duplication accumulates not because generated code is careless but because deduplication is the operation the context shape does not support.

Two secondary effects push the same direction. Suggestions are accepted faster than typed code is written, so the moment where a developer would have thought “haven’t I written this before” is compressed. And a model asked to follow the surrounding style will reproduce a nearby pattern faithfully, which is exactly what you asked for and also how a copied block gets a fourth sibling.

The second metric: rework

Rework — code changed again shortly after it was merged — is the more interesting of the two, because it is a proxy for whether the code was understood when it landed. Code you wrote and reasoned about tends to survive; code that was accepted because it looked right tends to come back.

Rework is also a metric that can move for innocent reasons: more experiments, faster iteration, a team that ships small changes deliberately. So it is a trend to watch on your own repository against your own history, not a number with a good or bad value. A rising rework rate alongside a rising commit rate may be exactly what you wanted; a rising rework rate with flat delivery is a warning.

There is a third kind of debt that neither metric captures, and it may be the one that matters most: nobody on the team has ever held the code in their head. Traditional debt is code somebody understood when they wrote it and nobody has revisited since. This is code that was never understood by anyone at any point — it passed review, it passed tests, and the model that produced it has no memory of it either. The cost appears the first time it must be changed under time pressure, it is invisible to any static metric, and it is the reason the review discipline further down is not optional.

What the published analysis is

One source is cited almost everywhere in this discussion: GitClear’s code-quality reports (the 2024 analysis and its 2025 follow-up), which examine a large corpus of changed lines across public and private repositories and report a rising share of duplicated blocks, and a rising ratio of copy-pasted to moved lines over the period in which assistants became common.

It is worth being precise about what that is, because it is repeated as though it were a controlled study.

  • It is a vendor’s analysis of a corpus the vendor assembled, published as a report rather than through peer review. That does not make it wrong; it does mean the method is not independently reproduced.
  • It is correlational over time. Nothing was randomised and adoption is inferred from the calendar, so every other change in software between those years — team growth, framework churn, microservice fashion, the composition of the corpus itself — is confounded with it.
  • The metrics are defined by the tool. “Moved versus copy-pasted” is a diff-classification heuristic, and the classification is the measurement.

Read it as a plausible signal consistent with the mechanism above, reported by a party with an interest in the topic, and not as an established effect size. The honest position is that the mechanism is clear, the direction is plausible, and the magnitude in your repository is an empirical question you can answer in an hour.

Measuring your own repository

Duplication, tracked as a ratchet

# jscpd works across ~150 languages; PMD CPD and simian are alternatives
npx jscpd src/ --min-tokens 60 --reporters json --output .reports/

jq '.statistics.total | {percentage, duplicatedLines, clones}' \
   .reports/jscpd-report.json
# { "percentage": 4.7, "duplicatedLines": 3120, "clones": 218 }

# same trick as the refactor ratchet: a committed budget that may only fall
# ci/duplication.budget -> fail the build when the percentage rises

Run it once against a tag from before adoption and once against HEAD. Two points on your own codebase, measured the same way, are worth more than any industry figure — and the delta is attributable to your team rather than to the software industry.

Rework, from git alone

# Age profile of the lines currently in HEAD. Rework shows up as a bulge of
# very young lines that are replacing other young lines.
git ls-files 'src/**/*.ts' | while read -r f; do
  git blame --line-porcelain -- "$f" | awk '/^author-time /{print $2}'
done | sort -n > /tmp/line-ages.txt

# share of lines written in the last 21 days
cutoff=$(( $(date +%s) - 21*86400 ))
awk -v c="$cutoff" '{n++; if ($1 > c) y++} END {printf "%.1f%% younger than 21d (%d/%d)\n", 100*y/n, y, n}' /tmp/line-ages.txt

# and the churn view: files rewritten most often in the last quarter
git log --since='90 days ago' --format= --name-only -- src/ \
  | sort | uniq -c | sort -rn | head -20

Compare the same quarter a year apart rather than looking at one number. And restrict to hand-maintained source: generated clients, migrations and fixtures will dominate any churn ranking and none of them are debt.

What actually prevents it

  • Put the existing helpers in the context. This is the direct fix for the direct cause. A ranked signature index means the model can see that formatMoney exists; building one pays for itself here as much as in accuracy.
  • Make “does this already exist?” a review question. One line on the checklist, applied to every new function in a generated diff. A thirty-second grep answers it, and it is the single review question with the best return on AI-authored code.
  • Ratchet duplication in CI so the number can only fall. A threshold that fails a build converts a slow drift into a visible decision at the moment it is made.
  • Do not let an agent land its own work. The accumulation happens in the gap between generating and understanding, and human review is the only thing that occupies that gap.
  • Prefer codemods for anything repetitive. A transformation applied 400 times produces uniform code by construction; the codemod argument is a debt-prevention argument as much as a review one.

One thing that is not debt, and gets counted as it: generated code nobody maintains. A protobuf client, an OpenAPI SDK, a migration file. Duplication in those is free, because the fix for a defect is in the generator and no human reads the output. Debt is only debt where a person will one day have to understand the code — measure the hand-maintained tree and exclude the rest, or your numbers will move for reasons that do not matter.

Technical Debt From AI-Generated Code · Multigrid