Engineering Practitioner Brief / 18 May 2026

Dead Code Cost

The annual tax of carrying code that nobody runs.

Dead code is the politest form of technical debt. It does not break anything, it does not cause incidents, it does not show up on dashboards. It just makes every other piece of work slightly slower, every CI bill slightly higher, and every security scan slightly noisier. The cumulative cost per engineer per year sits between $8,000 and $60,000 depending on codebase size, language, and how much of the dead code is in hot read paths. This page breaks down where that range comes from and how to act on it.

15%

Median unused-function rate

Romano and Scanniello, 2018, Java sample

$8K-$60K

Annual cost per engineer

Range, varies by language and codebase size

2 to 6 hrs

Removal cost per 1,000 dead LOC

Median, with tooling and a short observation window

The Five Cost Vectors of Dead Code

Dead code is not free because it is shipped to production. Every line carries a small recurring tax. Each vector is individually modest, but they compound because the same line gets read, indexed, built, scanned, and reasoned about thousands of times across a codebase's life.

Vector 1: Read-time cognitive tax

Engineers spend the largest single share of their time reading code, not writing it. Microsoft Research has published instrumentation studies putting the ratio at 6:1 to 10:1 in favour of reading. When an engineer changes a function that calls helper-A, they also read helper-A, B, and C nearby. If half of those helpers are dead, the engineer still reads them because the IDE does not visually distinguish reachable from unreachable code by default. Conservatively this adds 3 to 8 minutes per dead helper per code-change interaction. Across a year of 200 working days and 5 to 10 such interactions per day, the per-engineer cost ranges from $1,500 to $4,500 at typical fully-loaded rates.

Vector 2: IDE and code-search latency tax

Every language server, every IDE indexer, every grep across a repo, every code-search tool (Sourcegraph, GitHub code search, internal greps) has to walk the dead code. For repos in the 100K to 1M LOC range the marginal cost of an extra 15 percent dead code is measurable: 1 to 3 seconds added to project-wide find-usages, 5 to 15 seconds added to a fresh language-server warmup, 200 to 800 ms added to symbol-search queries. Over a year these stack up to $400 to $1,800 per engineer.

Vector 3: CI build and test minutes tax

Dead code compiles. Dead modules sit in the dependency graph. Dead tests run (because the test files themselves are not dead, even though the production code they cover is). For a TypeScript monorepo, dead code typically adds 8 to 25 percent to a cold-cache full build. Tree-shaking in the bundler removes dead code from the production artifact but does not remove it from CI compilation, type-checking, or test runs. At typical GitHub Actions or CircleCI minute pricing (see dependency debt cost for related infra components), the per-team annual cost is in the $5,000 to $25,000 range for a team of 10 engineers, or $500 to $2,500 per engineer.

Vector 4: Security and compliance scan tax

Snyk, Dependabot, Semgrep, GitHub Advanced Security, and SAST tools scan every line. Dead code accounts for a proportional share of the false-positive triage workload. A 15 percent dead-code rate produces roughly 15 percent more security alerts to look at, even though almost none of the alerts on dead code are exploitable. The triage cost per false-positive averages 10 to 30 minutes when documenting the false-positive justification for compliance auditors. For a team running a serious AppSec program, this is the second-largest cost vector after read-time tax.

Vector 5: On-call and debugging tax

When an alert fires at 3am and the on-call engineer searches the codebase for the function name in the stack trace, dead code shows up alongside the real implementation. The wrong path gets read first more often than the right path because dead code tends to be the older, more deeply-nested implementation. The DORA State of DevOps reports consistently link low code quality to longer mean-time-to-recover; a portion of that MTTR penalty is the time spent reading dead code during incident triage. Per engineer per year this lands at $500 to $2,000 depending on on-call frequency.


A Worked Removal Example

To make the cost-of-removal concrete, consider a 250,000-LOC TypeScript monorepo with 12 engineers. A dead-code audit using Knip and runtime instrumentation identifies the following:

CategoryDead LOC FoundRemoval EffortTooling Used
Unused exports8,40014 hrsKnip + ts-prune
Stale feature flags3,10028 hrsLaunchDarkly export + manual
Deprecated REST endpoints2,20022 hrsAccess-log analysis
Vendored libs no longer imported11,5006 hrsnpm ls + delete
Sunset product modules14,00040 hrsManual with stakeholder review
Total39,200 LOC110 hrs

The removal effort cost at $85 per engineer-hour is $9,350. The annual carrying-cost savings against the midpoint of the per-engineer dead-code tax (call it $25,000) applied to 12 engineers is $300,000. The payback period is less than two weeks. The biggest reason teams do not run this audit annually is not the cost or the risk, it is that the audit does not ship a feature and therefore does not appear on most roadmaps.


How to Get Dead Code Off the Roadmap-Less List

Three patterns work in practice. The first is the standing 5 percent rule: every sprint, every engineer spends 4 hours on dead-code removal. No approval needed, no roadmap slot. The cumulative effect across a team of 12 is roughly 200 engineer-hours per quarter, which clears a 250K LOC monorepo of its dead-code tax over 18 months on a continuous basis.

The second is the audit-and-PR pattern: once a year, an engineer or small team runs the full audit, opens PRs grouped by category, and gives owners two weeks to object before the delete commits land. This is more efficient than the standing rule but harder to start because the up-front audit cost is visible.

The third is bundling: every legacy-code refactor (see legacy code refactoring cost) includes a mandatory dead-code sweep of the surrounding module. This bundles the cost into work that is already approved and exploits the fact that the engineer has the context loaded. It is the slowest pattern globally but the lowest-friction politically.


Language-Specific Detection Notes

Detection quality varies sharply by language. Static type systems and explicit imports make detection more reliable; dynamic dispatch, reflection, and string-name lookup make it less so.

Related Reading


Frequently Asked Questions

What counts as dead code?

Any code that is shipped to production but never reached by any execution path. Common categories: unused functions, unreached branches behind permanently-false feature flags, deprecated endpoints with no callers, vendored libraries no longer imported, and entire modules left over from products that were sunset but never deleted.

How does dead code cost money?

Five vectors: cognitive load when engineers read it during nearby changes, slower IDE response and code search, longer CI build and test times, more surface area for security scanners to flag, and on-call confusion when production behaviour does not match the code an engineer is reading.

How much dead code is normal?

Industry studies suggest 5 to 30 percent of a typical codebase is dead, with the percentage rising sharply after 5 years of activity and after major refactors that leave callers behind. A 2018 study by Romano and Scanniello found a median of 15 percent unused functions across a sample of Java repositories.

Is it safe to delete dead code?

Safer than people think, less safe than tools claim. Static analysers miss code reached via reflection, dynamic dispatch, or string-name lookup. The standard approach is to instrument the suspect path with logging for two weeks, confirm zero hits, then delete in a single isolated commit that can be reverted.

Which tools find dead code reliably?

Knip and ts-prune for TypeScript, Vulture for Python, the Roslyn analyser pack for C#, unused-deps for Rust, deadcode for Go (golang.org/x/tools/cmd/deadcode), and Scalyr or Honeycomb plus structured logging for runtime confirmation. Coverage tools used as dead-code detectors are noisy but quick.

Should I delete a feature flag that has been false for two years?

Yes. Stale feature flags are one of the highest-yield dead-code categories because they typically guard 50 to 500 lines each. LaunchDarkly's own data, published in their 2023 Feature Flag Lifecycle report, shows flag-deletion rates well below creation rates across nearly all customer cohorts.

Updated 2026-04-27