← All writing
Craft · · 7 min

Deleting code

Four ways to find out what's actually dead, and how to put a subtraction on an invoice.

Agency Culture

Three days in November, on a maintenance retainer, spent almost entirely deleting.

A campaign microsite section from 2016. An integration with a mailing platform they stopped paying for in 2018 and which was still initializing on every page load. Two JavaScript libraries. A stylesheet for a print layout that had been superseded by a better one and never removed. About 900 lines of CSS. A cron job that emailed a report to somebody who left.

The site does exactly what it did before. It’s 180 KB lighter, it makes four fewer third-party requests, and there are two fewer things that can break at 3 am.

The client asked what they’d got for the three days. Not unkindly, not suspiciously. It’s a completely fair question and it’s the question that makes this work vanish everywhere it vanishes, which is nearly everywhere.

The incentives are backwards

Nobody deletes anything, and I don’t think it’s laziness. The payoff structure is genuinely hostile.

If you delete something and nothing happens, which is the successful outcome, nothing happens. There’s no demo. There’s no screenshot. Nobody in the business notices, because the entire point is that no one was using it.

If you delete something and it turns out one person was using it, you broke the site, and it’s identifiably your fault, and there’s a commit with your name on it.

So the expected value of deleting is a small invisible gain against a small visible catastrophe, and every rational person leaves it alone. Multiply that by four years and a couple of staff changes and you get every codebase I have ever inherited.

Deleting has a small invisible upside and a small extremely visible downside, so everybody rationally leaves it alone, and every codebase becomes an attic.

The other reason, which I wrote about when I was arguing myself into utility classes, is that you can’t prove a thing is unused. Not from the code. The class might be typed into a content field. The endpoint might be hit by a script somebody wrote in 2017. Absence of evidence, and all that.

Which is true, and it’s the reason to be careful rather than the reason not to bother, because “we can’t be certain” is how an attic happens.

Four ways to find out what’s dead

Ask the analytics. Not about code, about features. The events page that had 11 visits this year. The filter nobody uses. The account area with 40 registrations, 38 of which are staff. This is the highest-yield source and it belongs to the client, not to you, which makes it persuasive in a way your opinion isn’t.

Use the coverage tooling. The browser will tell you which bytes of your CSS and JavaScript actually ran on a given page. It’s a per-page snapshot rather than a verdict, so you have to walk the site and accumulate, and it’s still the fastest way to find an entire stylesheet nobody has needed since 2018.

Search everywhere, not just the repository. This is the step people skip. Grep the codebase, obviously, and then also search the CMS content, because a class name somebody typed into a WYSIWYG in 2015 is invisible to every tool you own. On the November job that check saved me from deleting a callout style used on exactly one page, which was the page about their accreditation, which is the page their whole business hangs off.

And when you can’t tell, instrument it. Add a line that records a hit, ship it, wait a month, look. This is the honest professional answer and it’s the one I use for anything I’m nervous about. A month is nothing on a four-year-old codebase, and “this endpoint was called zero times in November” is an argument nobody can counter.

Delete in its own commit

The safety rail that makes all of this low-risk, and it’s the thing I’d insist on.

A deletion goes in a commit that does nothing else. No tidying up while you’re in there, no small fix, no rename. Just the removal, with a message saying what was removed and how you established it was dead.

git log
Remove the 2016 campaign templates and their assets

Six templates, a stylesheet and eleven images from the fall 2016
push. The routes 404'd already; these were orphaned when the URLs were
retired. Logged for four weeks and got zero hits.

If something turns out to have referenced these, revert this commit
and nothing else is affected.
Text

That last line is the whole trick. An isolated deletion is the single most reversible change in software: one revert, seconds, no untangling. It’s a mixed commit that makes it frightening, because then reverting the deletion also reverts three other things you wanted.

Which means the fear people have about deleting is nearly always fear of the process, not of the deletion, and the process is fixable.

Putting it on an invoice

Back to the client’s question, since I’ve now had time to think of the answer I should have given.

The failure was describing it in terms of the work. “Removed unused code” is a description of my afternoon, not of a benefit, and I’d be skeptical of it too.

The version that works is risk and speed, in their terms:

“We removed a connection to a mailing service you stopped using two years ago, which was still loading on every page. That’s four fewer outside companies your site depends on to render, 180 KB less for every visitor to download, and two fewer things that can break outside office hours. It also means when we do the security updates in January, there’s less to update.”

Every clause in that is a thing the client can evaluate. None of it mentions code.

And I’d put it on the invoice as its own line, deliberately, rather than folding it into “maintenance,” because a line item is a thing that exists and gets discussed, and a fold is a thing that gets cut when budgets tighten.

What’s most worth deleting

In my experience, in order: dependencies, because each one is an update obligation and a supply chain you didn’t audit. Then features, because each one is a support surface and a thing that has to keep working forever. Then configuration and feature flags for experiments that concluded. Then dead CSS, which is the biggest by volume and the least important by risk.

Commented-out code doesn’t even count. Delete it on sight. That’s what git is for and it has been for 15 years.

I’d also say, at the risk of sounding odd about it: this is the most enjoyable work I do. There’s something lovely about ending a week with a codebase that is smaller and does the same thing, and I don’t get that feeling from anything I add.

Which is probably why I keep looking for ways to describe it that make it sound like a deliverable. It is one. It’s just the only one that arrives as a subtraction.

Read similar posts
7 min

Strangler fig

The pattern has a name and a literature, and the literature is about banks with 40 teams, and I have a 40-page site for a firm of surveyors, and the mechanics scale down further than anybody writes about.

7 min

The rewrite that shouldn't happen

This is the third proposal to rewrite the same system in five years, all three from competent people acting in good faith, and the interesting question isn't whether they're right, it's why the proposal keeps arriving.