I was there when we deleted 50,000 lines of "critical" Java code. The system got 40% faster. Deployment time dropped. The team's velocity doubled. In 45 years of engineering, that deletion was my proudest moment. Sometimes the best engineering is subtraction.
Schedule regular code deletion reviews. If code hasn't been touched in 2 years and isn't tested, delete it. Maintenance cost compounds; deletion is a feature.
The problem is that nobody gets promoted for deleting code. We're trained to build. Add features, add systems, add complexity. According to software engineering research, maintenance typically accounts for 60-80% of total software lifecycle costs - far exceeding initial development. Yet the most impactful changes I've made have often been deletions. Here's what I've learned about the courage to subtract.
Why We're Attached to Code
Deleting code is emotionally hard for reasons that have nothing to do with engineering:
Sunk cost. You spent weeks building it. Deleting it feels like throwing away that time. But the time is already spent. The question is whether keeping the code serves the future, not whether building it served the past.
Identity. You wrote this. It's yours. Your cleverness is embedded in it. Deleting it feels like deleting part of yourself. But you're not your code. Your value is your judgment, not your output.
Fear of needing it. "What if we need this later?" So you keep it, just in case. Months pass. Nobody touches it. It becomes technical debt that someone will eventually have to understand, maintain, or work around.
Visibility of addition vs. subtraction. Adding features is visible. Shipping is celebrated. Removing code is invisible work. According to Google's SRE book on simplicity, the most reliable systems are the simplest ones. Nobody gets promoted for deleting things. The incentives are wrong.
Uncertainty about consequences. What depends on this code? What will break? Addition is safe - you know what you're adding. Deletion requires understanding the whole system.
The Real Cost of Keeping Code
Every line of code has ongoing costs:
Reading cost. Someone has to understand it. Every new team member has to figure out what it does, whether it matters, how it interacts with everything else.
Maintenance cost. Dependencies update. APIs change. The code needs to keep working. As research on software complexity and maintenance costs shows, even "finished" code requires attention.
Cognitive load. The more code exists, the more mental models developers need. Complexity compounds. Simple changes become hard when you have to understand everything.
Bug surface. Code that exists can have bugs. Code that doesn't exist can't. The safest code is no code.
Testing burden. Tests need to cover it. CI runs take longer. Test maintenance grows with code size.
Opportunity cost. Time spent maintaining dead code is time not spent on valuable work.
The cumulative cost of keeping code often exceeds the cost of building it in the first place. This is technical debt in its purest form - liabilities masquerading as assets.
Stories of Successful Deletion
Three deletions that improved everything:
The 50,000 Line Legacy System
At one of my companies, we inherited a "critical" subsystem - 50,000 lines of Java that handled a complex business process. Everyone was afraid to touch it. The original author had left. Documentation was sparse.
I spent two weeks understanding what it actually did. The answer: it solved a problem that no longer existed. The business process had changed years ago. The system was running, processing data, producing outputs that nobody used.
Deleting it required courage. What if I was wrong? What if something actually depended on it? I built monitoring to detect any access to its outputs. Nothing. For a month, nothing.
We deleted it. Deployment time dropped by 40%. The codebase became navigable. Developers stopped asking "what does this do?" about something that did nothing.
The Abstraction That Wasn't
An early architect had built a "flexible" data layer. Any storage backend could be swapped in. We supported SQL, NoSQL, file systems, and in-memory storage. Beautiful abstraction. I've seen this exact pattern at three different companies I've worked with.
In eight years, we used exactly one backend: PostgreSQL. The abstraction added complexity to every data operation. New developers had to understand three layers of indirection to write a simple query. This is exactly why PostgreSQL wins - it does enough that you don't need abstract switching layers.
Removing the abstraction was a month of work. Replacing it with direct database calls was straightforward. The result: 60% less data access code, clearer error messages, easier debugging, and queries that were actually optimizable. This is the layer tax in action - every unnecessary abstraction costs you.
The abstraction had been built for a future that never arrived.
The Feature Nobody Used
Our product had an "advanced mode" with 40+ configuration options. Product managers loved it - so much flexibility. Users could customize everything.
Analytics told a different story. 3% of users ever opened advanced mode. Of those, 90% changed one setting and never returned. We were maintaining 15,000 lines of code for a feature that effectively nobody used.
The hard part wasn't deleting the code. It was getting organizational buy-in. Product had spent months designing those options. Deleting them felt like admitting failure.
We deleted it. Support tickets dropped. The interface became simpler. The 3% who used advanced mode complained briefly, then adapted. Net improvement.
Signs Code Should Be Deleted
Patterns that suggest subtraction over maintenance:
"Nobody knows what this does." If the entire team is afraid to touch something, it's either critical infrastructure (document it) or dead code (delete it). Usually the latter.
"We might need it someday." Version control exists. If you need it, you can retrieve it. The "someday" that justifies keeping unused code almost never comes.
"It works, don't touch it." Working but unmaintained code is a liability. Eventually it will break, and nobody will know how to fix it.
Abstraction for one implementation. Interfaces with single implementations, factories that create one type, configurability that's never configured. These are complexity without benefit.
Features with near-zero usage. Analytics don't lie. If nobody uses it, nobody needs it. The exceptions are rare enough that you should prove the exception before assuming it.
Commented-out code. If it's been commented out for more than a week, delete it. Version control remembers. Comments don't help.
Should This Code Be Deleted?
Score the code in question:
How to Delete Safely
Deletion requires care. Some practices that help:
Understand before deleting. Trace the dependencies. Understand what calls this code, what it calls, what data it touches. Deletion without understanding is recklessness.
Monitor first. Add logging or metrics to understand actual usage. Let the data tell you whether code is dead. Assumptions are dangerous.
Delete incrementally. Remove callers first, then the code. Each step is reversible. Big-bang deletions are risky.
Keep tests until the end. Tests document behavior. Delete the implementation, watch what breaks, then delete the tests.
Communicate. Tell the team what you're removing and why. Someone might know something you don't. Or they might just need to update their mental model.
Time-box the fear. Set a date. "If nothing breaks by March, we delete it completely." Living with dead code "just in case" forever isn't a strategy.
Organizational Barriers
The hardest part of deletion is often organizational:
Nobody gets credit. Performance reviews reward shipping. Deleting code is invisible work that makes future work faster. The incentives don't align.
Stakeholder attachment. Someone championed that feature. Their career advancement depended on it. Deleting it feels like criticism of their judgment.
Fear of responsibility. If something breaks after deletion, the person who deleted it is blamed. If something breaks because of kept complexity, nobody is blamed. The asymmetry encourages hoarding.
"Just in case" culture. Risk-averse organizations keep everything. The cost is diffuse and ongoing. The risk of deletion is concentrated and visible.
Overcoming these barriers requires leaders who value simplicity and are willing to celebrate deletion as much as addition.
The Courage to Subtract
Deletion requires a kind of courage that addition doesn't:
Admitting uncertainty. You can't be 100% sure nothing will break. You're making a judgment call with incomplete information. That's uncomfortable.
Challenging the past. Deleting code implies someone made a mistake - the person who built it, the people who kept it. Nobody wants to say "this shouldn't exist."
Taking responsibility. If deletion causes problems, you're accountable. It's easier to leave things alone and let shared entropy diffuse the blame.
Resisting attachment. Sometimes you have to delete your own code. The feature you were proud of, the abstraction that was clever. Killing your darlings is hard.
The engineers I respect most are the ones who can look at something they built and say "this was wrong" or "this is no longer needed." I've had to do this with my own code more times than I can count - at MSNBC, at ZettaZing, at every company I've built. That's growth. That's judgment. That's what seniors do.
A Mindset Shift
Two mental models that help:
Code is a liability, not an asset. Every line costs something to maintain. The question isn't "can we keep this?" It's "is this worth its ongoing cost?" The default should be deletion, not retention.
Simple systems win. The systems that survive decades are simple. They do less. They're comprehensible. Complexity is a tax on everything. Simplicity is the goal.
The best engineers I know are aggressive deleters. They look at working systems and ask "what here doesn't need to exist?" They understand that subtraction is a form of improvement.
Code Deletion Readiness Scorecard
Score the code you're considering for deletion. High scores mean it's probably safe to remove.
| Dimension | Score 0 (Keep) | Score 1 (Investigate) | Score 2 (Delete) |
|---|---|---|---|
| Last Modified | Within 6 months | 6-18 months ago | >18 months untouched |
| Team Knowledge | Someone understands it | Partial understanding | "Nobody knows what this does" |
| Usage Metrics | Active traffic/calls | Sporadic usage | No usage in production logs |
| Test Coverage | Well-tested, tests pass | Partial coverage | Tests disabled or failing |
| Abstraction Scope | Multiple implementations | Two implementations | Interface with single implementation |
| Documentation | Current and accurate | Outdated but exists | "We might need it someday" |
The Bottom Line
Building is celebrated. Deleting is necessary.
The codebases that remain maintainable over years are the ones where someone had the courage to remove what wasn't needed. The systems that scale are the simple ones. The teams that move fast are the ones with less to understand.
If your proudest engineering moments are all about adding things, you might be missing half of the discipline. The best code is often no code. The best feature is often no feature. The best system is often the simpler one.
Sometimes the right answer is delete.
"The best engineers I know are aggressive deleters. They look at working systems and ask "what here doesn't need to exist?"
Sources
- PMC: Which Factors Affect Software Projects Maintenance Cost More? — Research showing maintenance accounts for 60-80% of total software lifecycle costs
- IEEE: Dead Code Detection and Removal — Research on automated dead code detection showing significant maintenance cost reduction after removal
- Goldman Sachs: Don't Let Dead Code Satisfice — Engineering blog on the hidden costs of keeping unused code in production systems
- Google SRE: The Virtue of Boring — Google's Site Reliability Engineering principles on simplicity and removing unnecessary complexity
Engineering Excellence
Sometimes less is more. Architecture review from someone who's learned to let go.
Discuss