Skip to content
Rezha Julio
Go back

The Art of Deleting Code: Why the Best PRs Have Negative Lines

5 min read

I love opening a Pull Request where the lines deleted outnumber the lines added. There’s something genuinely satisfying about it. But not everyone sees it that way.

Most engineering cultures still celebrate addition. The more code you ship, the more “productive” you look. But every line you write is a liability. It has to be maintained, tested, and debugged for as long as it exists. Software Engineering at Google says it well: “Code itself doesn’t bring value: it is the functionality that it provides that brings value.” If you can get the same result from 10 lines instead of 10,000, the 10-line version is better in every way that matters.

Dijkstra saw this clearly back in 1988. In On the Cruelty of Really Teaching Computing Science, he wrote: “If we wish to count lines of code, we should not regard them as ‘lines produced’ but as ‘lines spent’: the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger.” Bill Gates put it differently: “Measuring programming progress by lines of code is like measuring aircraft building progress by weight.”

Code is a liability

When you’re starting out, you think your job is to write code. After a few years, you realize your actual job is to solve problems, and code is just the medium. The less of it you need, the better.

A new line of code can introduce a new bug. A new dependency is a new security risk. A complex architecture makes it harder for the next person to get up to speed. All code has to be read, understood, and maintained by someone, and that someone might be you, six months from now, wondering what you were thinking.

Ken Thompson, co-creator of Unix, reportedly said: “One of my most productive days was throwing away 1,000 lines of code.” Jeff Atwood put it this way: “The best code is no code at all. Every new line of code you willingly bring into the world is code that has to be debugged, code that has to be read and understood, code that has to be supported.”

Why we resist deleting code

It’s worth understanding why we hold on before talking about when to let go.

The obvious one is fear. “What if something depends on this?” So we comment it out and leave it there “just in case.” But that’s what version control is for. Git remembers so you don’t have to.

Then there’s the emotional side. You spent hours on that solution. Deleting it feels like throwing away work. But this is textbook sunk cost fallacy: the effort you already put in doesn’t change whether the code is still useful.

And sometimes you keep code because you don’t fully understand what it does. Nobody dares touch it, nobody bothers to investigate, because it’s not causing problems. Yet.

When to delete

If your analytics show nobody has used a feature in six months, delete it. Don’t save it “just in case.” Git has history; that code isn’t truly gone. According to a Built In report from March 2026, up to 40% of engineering capacity can go toward maintaining “zombie features,” legacy code used by fewer than 5% of customers. One company they profiled was spending $200,000/year to support a feature that brought in only $15,000.

If you once built a big abstraction to handle use cases that never showed up, rip it out. Replace it with something simple. The best abstraction is the one you don’t have to maintain.

If a modern language API can replace an external dependency, drop the dependency. Smaller bundle, smaller attack surface.

And if you see commented-out code sitting in the repo, delete it. It’s noise. It confuses anyone reading the file and creates the illusion that it might still matter. It’s in version control. You can get it back if you ever need it.

What actually changes when you delete code

One team reported a 30% performance improvement after cutting a batch of legacy functions. Bundle sizes shrank, builds got faster, and tests ran quicker.

Beyond performance, there’s the human side. New engineers ramp up faster when there’s less code to wade through. Debugging is easier when there are fewer files and fewer code paths. And honestly, people are just happier working in a clean codebase. Nobody wants to spend their day tiptoeing around code that shouldn’t be there.

Bloated systems slow teams down in ways that are hard to measure but easy to feel. More time navigating old dependencies, more edge cases to test, more time spent understanding behavior that no longer matters. Removing that weight makes everything move faster.

How to do it without breaking things

You don’t have to be reckless. Run coverage tools and static analysis. ESLint’s no-unused-vars, TypeScript’s strict mode, and test coverage reports can point you to dead code quickly.

Delete in small chunks. One module, one feature at a time. Run your tests after each removal. Small, verifiable deletions are much safer than sweeping cleanups.

Branch first, so you can revert easily. And if you’re unsure whether something is actually in use, check your feature flags and production analytics before you touch it.

Celebrate the minus PR

Next time you’re reviewing code, pay attention when someone submits a PR with +50 -500. That person just made the codebase easier to work in for everyone on the team.

Cory Doctorow wrote about this earlier this year: code’s capabilities are the asset, but the code itself is a liability. The longer it sits there, the more expensive it gets. The goal was never to write the most code. It was always to deliver the most value with the least.

Delete that code.


Related Posts


Previous Post
Nobody Gets Promoted for Simplicity