Why codebases now grow faster than they improve
It is cheaper to generate a new 200-line helper than to find the existing one, read it, and decide whether it fits. So duplication rises, and nothing in the process pushes back.
The asymmetry is structural rather than cultural. Adding is easy and locally justified: the new code works, the tests pass, the reviewer has no reason to object. Deleting requires knowing that nothing depends on it, which requires understanding the whole system, which is the expensive thing. So the default direction is accumulation, and consolidation only happens if somebody schedules it.
Previously there was an accidental brake: writing code was slow enough that people reused things out of laziness. That brake is gone. Nothing replaced it.
Find the duplication mechanically
You do not have to read the codebase to know where the copies are.
# copy-paste detection across most languages, free
npx jscpd src/ --min-lines 8 --min-tokens 60 --reporters console
# the cheap version: which function names exist several times?
rg -o '^\s*(def|function|func) (\w+)' -r '$2' --no-filename \
| sort | uniq -c | sort -rn | head -30That second command takes four seconds and routinely finds three date formatters, two retry helpers and four ways of building the same URL. Run it on your own repository before reading further; the result is usually the most persuasive argument in this lesson.
Find the dead code mechanically
vulture src/ # Python
npx ts-prune # TypeScript unused exports
npx knip # unused files, deps and exports
deadcode ./... # Go
cargo +nightly udeps # unused Rust dependenciesThese produce false positives (anything called by reflection, by a framework’s convention, or from a configuration string) so treat the output as a list to check, not a list to delete. Even so, a first run on a codebase that has never had one usually finds several per cent of the files.
The strongest version, if you can do it: coverage instrumentation in production for a week. Code that never executes under real traffic is a much more reliable signal than static analysis, and it catches the endpoint that no longer has any callers.
Make it a scheduled event
Consolidation loses every fair fight against a feature, so do not have the fight. Book the time.
The blunt version that works: one day a month, everybody, deletion only. No new features. The rules make it work:
- Deleting code counts. Deleting a dependency counts double.
- Merging three implementations into one counts.
- Removing a stale feature flag counts.
- Adding anything does not count, unless it is a test that lets you delete something.
Measure it and publish the number, because it is the one day where lines-of-code-removed is a legitimate metric. A team that removes 4,000 lines and two dependencies in a day has done more for next year’s velocity than a normal feature day, and saying so out loud is what makes people take the next one seriously.
The argument to management
Do not argue about elegance. Argue about the numbers you are already tracking.
Four date formatters means a date bug has to be fixed four times, and last quarter we fixed it twice and missed two. Every duplicate is a permanent multiplier on the cost of every future change in that area, and on the probability that a fix is incomplete.
Deleting the abandoned reporting module removes 6,000 lines, two dependencies with open advisories, and one database table we back up nightly. It reduces our attack surface and our review surface, and it takes one day.
Security and incident arithmetic persuade where aesthetics do not, and both are true.
The habit that prevents most of it
One norm, in the contributing guide:
Before adding a helper, search for one. If you find something close, either use it or write one sentence in the pull request saying why it did not fit.
That sentence is the whole control. It costs the author thirty seconds, it gives the reviewer something specific to check, and it is the only point in the process where the duplication decision is visible to anybody.
And the corresponding norm for the other direction: every feature flag gets a removal date when it is created, and the removal ticket is created at the same time. A codebase with two hundred stale flags has a configuration space nobody can reason about, and each individual flag was obviously fine to leave.
Every lesson here is open. Your progress is recorded once you pass the AI at Work exam.
Check your understanding
A team runs a duplication detector and finds four near-identical retry helpers, one of which retries non-idempotent POST requests. What is the strongest argument for consolidating them?
Pick the one you would defend. Your answer stays private.
Stuck on something?Ask Larissa, your AI tutor
