The Art of Deletion
Deleting autogenerated code
Throughout my career, I’ve found that some of my most impactful work involved deleting code rather than writing it. Systems often run faster and become easier to maintain when we remove unnecessary complexity.
This contradicts everything I learned early in my career. Fresh out of school, I believed shipping features meant success. More code, more functionality, more value—right? The industry reinforced this: sprint velocity, commit counts, feature flags.
But production systems don’t care about your velocity. They care about reliability, debuggability, and operational simplicity.
The Wake-Up Call: When Complexity Becomes the Enemy
My perspective shifted during various debugging sessions over the years. Often, performance issues stem from outdated optimizations—caching layers built for use cases that no longer exist, or defensive code that adds overhead without benefit.
Removing such cruft often improves performance significantly.
This pattern repeated itself across the codebase. Defensive logging that generated gigabytes of noise. Retry logic layered on retry logic. Configuration toggles for features that never launched. Each addition had seemed reasonable in isolation, but together they formed a maze.
Here’s what AI coding assistants have made worse: generating plausible-looking code is now trivial. Need a helper function? LLM provides five implementations. Need a config parser? Here’s a robust solution with validation, caching, and extensibility.
The hard part isn’t generating code anymore. It’s deciding what deserves to exist.
Archaeology in Production: Unearthing Dead Code
When I joined my current team at Meta, I inherited a service with several dependencies. Each dependency carried weight: security patches to track, breaking changes to monitor, mental overhead for new engineers trying to understand “why do we have three ways to make HTTP calls?”
The trickiest part wasn’t identifying what to remove—we ran dependency analysis and found dozens of unused imports. The challenge was building confidence that deletion was safe. Legacy systems accumulate defensive code: “I don’t know what this does, but I’m afraid to remove it.”
This fear is valid. I’ve seen production incidents caused by removing “dead” code that turned out to have one obscure caller. The solution isn’t reckless deletion—it’s systematic verification.
Building instrumentation to track which code paths actually execute in production is invaluable. You’d be surprised how much code exists that never runs—legacy paths from migrated features or defensive logic for scenarios that can’t occur anymore.
The Deletion Sprint: Making It Real Work
Here’s where most cleanup initiatives die: they get prioritized behind feature work, forever postponed as “technical debt we’ll address later.”
I took a different approach. I framed dependency cleanup not as technical debt, but as production risk reduction. The pitch to leadership was simple:
“Every unused dependency is a potential CVE waiting to happen. Every obsolete code path is a debugging session that takes 3x longer. This isn’t about code cleanliness—it’s about operational efficiency and security posture.”
That language resonated. We got a dedicated sprint.
My approach:
- Instrumentation First: Added telemetry to confirm zero usage in production
- Incremental Removal: One dependency per PR with full regression testing
- Rollback Plan: Feature flags for deletions affecting critical paths
- Documentation: Updated docs to explain what we removed and why
The benefits of aggressive pruning often exceed expectations. Fewer dependencies mean faster builds, smaller deployment artifacts, and reduced security surface area. You might be patching CVEs in libraries you’re not even using.
Incident response becomes more straightforward too. When you have multiple ways to do the same thing, every error requires checking multiple implementations. Consolidation makes diagnosis deterministic.
AI Amplifies the Problem
Generative AI has transformed how we write code, but it’s made the accumulation problem exponentially worse. LLMs are trained on massive codebases and optimize for completeness, not minimalism.
Ask ChatGPT to “implement user authentication” and you’ll get a full OAuth flow with JWT refresh tokens, role-based access control, and database migrations. Maybe you just needed to check an API key.
I’ve reviewed pull requests where engineers used AI to generate solutions that duplicate existing functionality—reinventing utilities that already exist in the codebase or standard library, or building complex abstractions for simple problems.
The code works. It’s even well-commented. But it’s unnecessary weight.
My code review filter now includes:
- “Can we use stdlib instead?” Modern languages have rich standard libraries. We don’t need lodash for array manipulation.
- “Does this solve a real problem?” Premature abstraction is worse than no abstraction. Build it when you need it.
- “What’s the maintenance cost?” Custom code means custom debugging, custom documentation, custom onboarding.
When AI suggests adding a dependency, I ask the engineer: “Have you checked if we already do this somewhere?” The answer is often yes.
The Senior Engineer’s Real Job
Early in your career, you prove yourself by shipping. You write features, fix bugs, close tickets. Your impact is measured in output.
But seniority isn’t about writing more code—it’s about making better decisions about what code should exist. Sometimes the most impactful contributions aren’t new features, but strategic removals:
- Merging redundant services to reduce operational overhead
- Deprecating configuration systems that have grown beyond their usefulness
- Consolidating similar utilities scattered across the codebase
These changes don’t show up in GitHub contribution graphs. They don’t make good demos. But they compound over time: faster CI/CD, easier onboarding, fewer production mysteries.
Here’s my framework for sustainable engineering:
- Addition requires justification. New code must prove its value.
- Deletion requires verification. Remove safely, but remove aggressively.
- Consolidation beats proliferation. One excellent solution beats five good ones.
AI has made creation trivial. The competitive advantage now belongs to engineers who can look at a sprawling system and know exactly what to remove to make it stronger.
Your legacy isn’t the code you wrote—it’s the maintainability you left behind.
TECH
Technical Debt AI Code Quality