Every line of code is a liability. It can break. It needs maintenance. It confuses new developers. The best code is code that doesn't exist.
Why We Don't Delete
Sunk cost fallacy. "I spent three days writing this."
Fear. "What if we need it later?"
Uncertainty. "I'm not sure if anything uses this."
Ownership. "Someone might be upset if I delete their code."
None of these are good reasons to keep code around.
Signs Code Should Go
It's commented out:
# def old_implementation():
# # This used to do something
# passDelete it. Git remembers.
It's behind a feature flag that's always off:
if ENABLE_LEGACY_FEATURE: # Never true
do_legacy_thing()Delete it. The flag too.
Nothing calls it:
def unused_helper():
"""Written for a feature that was never shipped."""
passDelete it. Your IDE can find unused functions.
It's a "just in case" abstraction:
class AbstractStrategyFactoryBuilder:
"""We might need this flexibility someday."""
passDelete it. YAGNI.
How to Find Dead Code
Static analysis:
# Python
vulture src/
# JavaScript
npx unimportedCoverage reports: Code that's never executed in tests might be dead.
Search for references:
grep -r "function_name" src/Feature flags audit: List all flags. Delete code behind permanently-off flags.
The Deletion Process
- Identify the code to delete
- Verify nothing uses it (grep, IDE, tests)
- Delete in a focused commit
- Test that nothing breaks
- Deploy and monitor
Keep deletion commits separate from feature work. If something breaks, reverting is easy.
Overcoming Fear
Git is your safety net. Deleted code lives forever in history. You can always get it back:
git log --all --full-history -- path/to/deleted/file.py
git checkout <commit>^ -- path/to/deleted/file.pyTests catch breaks. If your tests pass, the deletion is probably safe. If tests don't cover the code, maybe add them first—or accept the risk.
Production monitoring. If deleted code was actually needed, errors will tell you. Have good monitoring.
What to Delete First
Easy wins:
- Commented-out code
- Unused imports
- Dead feature flag branches
- Old migration files (after running)
- Deprecated endpoints (after removal period)
Medium effort:
- Unused utility functions
- Old API versions
- Legacy database columns
- Redundant abstractions
Requires planning:
- Entire unused services
- Old authentication systems
- Database tables
Deletion as a Practice
Schedule regular cleanup:
- Weekly: commented code, unused imports
- Monthly: dead functions, unused files
- Quarterly: unused features, old APIs
Make it part of the workflow. Don't let cruft accumulate.
The Payoff
Less code means:
- Faster builds
- Easier onboarding
- Fewer bugs
- Lower cognitive load
- Simpler refactoring
Every line you delete makes the codebase better.
My Rule
If I'm not sure whether code is used, I delete it and see what breaks.
Sounds reckless. But with good tests and monitoring, the risk is low and the reward is high. Dead code is noise. Noise slows everyone down.
Delete with confidence. Git has your back.