Why Deleting Code Is One of the Most Underrated Engineering Skills
by Eric Hanson, Backend Developer at Clean Systems Consulting
The Code That Shouldn't Be There
A developer is debugging a slow query. They trace it to a join on a table called user_preferences_v2. They look at the surrounding code to understand the schema. They notice there's also a user_preferences table. And a user_preferences_legacy table. All three are being queried in various parts of the codebase. Nobody currently on the team knows which one is canonical or what the other two are for.
This is the archaeological layer problem: codebases accumulate history. Features get deprecated but not removed. Experiments get abandoned but not cleaned up. Migrations complete but the old paths stay. Each layer is maintained, read, and sometimes misunderstood by everyone who touches the codebase afterward.
The three user_preferences tables represent three times as much schema to understand, three times as many queries to audit, and an ongoing risk that new code will interact with the wrong one.
What Deletion Actually Costs
Deleting code has a bad reputation for risk. The fear: what if something depends on this? What if removing it breaks something that wasn't obviously connected?
This fear is legitimate for code that is definitely in use. It is dramatically overstated for code that is clearly unused. Most codebases have a substantial amount of code that falls into easily-identified categories:
Feature flags that are permanently enabled: Code that was once conditionally executed based on a flag that has been true in production for two years. The false branch is dead code.
Deprecated endpoints with zero traffic: API endpoints that were marked deprecated, where metrics show zero calls in the last six months.
Dead code paths detected by coverage: Code that is never executed in production and never exercised in tests. Coverage tools can identify it.
Commented-out code: Code that was disabled by commenting rather than deletion, often "just in case." Git history preserves it permanently; it doesn't need to also exist in the file.
Duplicate implementations: Two functions that do the same thing, created at different points in the codebase's history, both still referenced.
For each of these categories, the deletion risk is low and the maintenance benefit is immediate.
The Discipline of Deletion
The precondition for confident deletion is the tooling to verify that something is unused:
Static analysis: Dead code detectors (IntelliJ's built-in analysis, PMD, SonarQube rules) can identify code that is never called from any reachable code path.
Production metrics: For API endpoints and features, traffic metrics confirm whether they are being used. An endpoint with zero requests in 90 days is a deletion candidate.
Feature flag state: If your feature flags are tracked in a registry, flags that are permanently enabled or disabled are candidates for code cleanup.
Database query analysis: Queries that reference tables or columns can be audited against database access patterns to identify columns that are never actually read.
With these tools, deletion stops being guesswork and becomes a measured activity.
The Process That Makes It Safe
Safe deletion is incremental:
- Identify the candidate using static analysis or metrics
- Verify by searching for all references (IDE find-all-usages, grep, import tracking)
- Shadow delete: For higher-risk cases, add logging at the call site for one to two weeks to confirm zero calls before removing
- Delete and ship: Remove the code, run tests, deploy
- Monitor: Watch error rates for a day or two after deployment
For low-risk deletion (dead code paths confirmed by analysis, commented-out code), steps 3 and 5 can be skipped. For higher-risk deletion (endpoints that might have undocumented callers, code paths with unusual calling patterns), the shadow delete provides confirmation.
The Cultural Problem
Deletion is undervalued for the same reason code is overvalued: metrics track code added, not code removed. A PR that deletes 500 lines and adds 50 looks like less work than a PR that adds 500 lines. The developer who deleted those 500 lines may have done higher-value work.
Teams that explicitly recognize deletion as engineering work — that treat "removed 2,000 lines of dead code" as a meaningful contribution — develop cleaner codebases. Teams that don't gradually accumulate the maintenance overhead of code that doesn't need to exist.
The Practical Takeaway
Schedule two hours in the next sprint specifically for deletion. Run your language's dead code detector. Look at your feature flags and identify any that have been fully rolled out. Check your API traffic metrics for any endpoints below 10 requests per day. For each candidate you find, verify and delete. Track the line count removed. The codebase will be measurably better and the work will have taken an afternoon.