We expected to find copy-paste.
Six popular JavaScript projects, the kind you've probably seen in a package.json somewhere. A lot of people say AI-generated code is piling up technical debt, and duplicated code is the usual first suspect. So we ran all six through the same five checks and waited for the duplication number to come back ugly.
It mostly didn't. The ugly numbers were somewhere else.
Why we went looking for duplication
The worry isn't made up. GitClear, a company that sells code analytics, looked at 211 million changed lines from 2020 to 2024. It reported that lines classified as copy-pasted rose from 8.3% to 12.3% between 2021 and 2024, and that 2024 was the first year copy-pasted lines outnumbered moved ones. Coverage of the report put the rise in duplicated blocks at about eightfold.
That is a trend across many repositories over time. What we did is different and much smaller: a snapshot of six mature projects on one day. It cannot say anything about AI. It can say what healthy, well-known code looks like right now, which is a useful baseline to hold your own repo against.
What we did
We picked six well-known JavaScript and TypeScript projects: a web framework, a faster web framework, a validation library, a build tool, a state library and an HTTP server. We cloned each one, scored its default branch, and ran the same five checks on every project.
- Cyclomatic complexity, counted per function
- Duplicated lines
- Test coverage
- Outdated dependencies
- Known vulnerabilities in the installed dependency tree
All six were scanned on October 15, 2026. We are not naming them. This is a baseline, not a leaderboard, and a score on one day says little about the maintainers who fix things every week.
Coverage came out empty for all six. None of them commit a coverage report, so a fresh clone has nothing to read. We left it out instead of inventing a number.
The code held up
Let's start with duplication. The share of duplicated lines ranged from 0% to about 20%.
The two highest looked scary until we opened the list. In the web framework, the duplicated blocks were test files that repeat each other. In the faster web framework, much of it was adapter files that mirror files elsewhere in the repo. The validation library, third highest, had one file per language. Still duplication, but it looks deliberate, not accidental.
Complexity was calmer than we expected. We flagged functions above a cyclomatic complexity of 10. Between 0.1% and 4.4% of functions crossed that line, depending on the project. A couple were real monsters: two projects had a function above 100. They're easy to point at, and a small share of the whole.
Nothing here makes a project good. But if you came in expecting a pile of copy-paste, there was no pile.
The dependencies were the problem
Then we looked at what each project depends on.
Between 8% and 51% of the dependencies we could check were a breaking release behind the latest version. In the worst project, about half of what it uses is a major version, or the equivalent, out of date.
Then the vulnerabilities. Four of the six commit a lockfile, so there's a real installed tree to audit. All four had known high-severity vulnerabilities somewhere in it, and two had critical ones. The other two don't commit a lockfile at all, so there was nothing for us to audit.
Put together, three projects got an F, one got a D-, one a B and one a B+.
Why a good project can get an F
A grade that looks at the full installed tree counts the tooling too: build, test and lint packages that never ship to your users. We didn't check how many of the vulnerable packages we found fall in that group, so don't read an F as "this library is dangerous".
We still think counting them is right. Your CI runs that tooling with your tokens and your source. But a low grade here means the project has more dependency debt than code debt, and that's where we'd look first.
What to check on your own repo
You don't need a tool for a first pass.
-
Count the breaking-behind dependencies. Run
npm outdatedand look at the Current vs Latest columns. Anything a major version behind is a breaking upgrade waiting for you. -
Audit the lockfile. Run
npm auditagainst it. If you have no lockfile, that's the finding. - Look at the lines your last pull request changed. Duplication and untested code hide in a diff much better than in a whole repo. A repo-level number can stay green while the new code is the worst in the project.
The third matters most for AI-written changes. Say a reviewer is reading 400 changed lines. The diff can't show that 90 of them already exist in another file, because it only shows what changed.
One disclosure
We build DebtDraft, a GitHub Action that runs these five checks and posts one score on every pull request. We used it to produce the numbers above, with the same settings on all six. Everything in the previous section works without it.
What would you check first on your own repo: duplication, dependencies, or the lines your last pull request changed?
Top comments (0)