← The ADLC library
Live debates · 23

Delete more than you think you can

Writing code got cheap. Deleting it did not, and its value went up. Deletion is now the highest-return operation in a codebase and the one your organisation rewards least.

Consider two changes landing on the same afternoon. One adds a feature: four hundred lines, a new endpoint, tests, a migration. The other removes an unused reporting path: minus nine hundred lines, no new behaviour, nothing a user will notice.

Ask which one your organisation celebrates. Then ask which one improved the balance sheet more. The answers have never matched, and the gap just got much wider.

My position: deletion is now the highest-return operation available in a codebase, and the reason is an economic asymmetry that arrived with agents. Everything else got cheaper. Removal did not, so its relative value went up, and almost no organisation has adjusted its incentives to match.

The asymmetry

Before, the two operations were roughly symmetric in cost. Writing a feature was expensive. Removing one was also expensive, because you had to establish that nothing depended on it, and that investigation cost real senior time. Both sides of the ledger were priced in the same currency, so it was a fair fight and addition usually won on merit.

That symmetry is gone. Generation collapsed toward free. The investigation that precedes a safe deletion did not, because it is not a generation problem. It is a question about the world outside the repository: is anyone calling this, does a customer depend on it, will a batch job fail at quarter end.

So the cost of adding fell by an order of magnitude and the cost of removing barely moved. Every organisation now runs a system where the accretion rate massively exceeds the removal rate, and nobody chose that. It fell out of the price change.

What got cheaper

  • Writing the implementation
  • Writing the tests
  • Writing the migration
  • Producing a second variant
  • Trying an approach and abandoning it

What did not

  • Proving nothing depends on it
  • Finding the customer who still uses it
  • Getting someone to authorise the removal
  • Being the person on call if you were wrong
  • Explaining a rollback to a stakeholder
Every item on the right is a question about the world rather than the code. That is why generation did not touch it and why it is now the binding constraint.

Every retained line is now a recurring cost

The classic case for deletion was about human attention: fewer lines to read, fewer places for a bug, less to onboard into. That argument was always true and always lost, because the cost was diffuse and the risk of removal was concentrated on one person.

There is now a second cost, and it is more concrete. Code in your repository is context. Agents working in your codebase read it, pattern match against it, and reproduce its shapes. A dead module is not inert any more. It is an exemplar, and it will teach the thing writing your next feature how your team does things, including the things your team stopped doing in 2023.

That converts every retained line from a static liability into a recurring one. You do not pay for it when someone reads it. You pay for it every time something generates against it, which is continuously.

I find this argument moves people who were unmoved by the attention argument, because it is mechanical rather than aesthetic. You are not asking a senior to tidy up. You are asking them to remove a bad training example from a system that will imitate it.

Why nobody deletes

The blockers are organisational, not technical, and they are worth naming individually because they have different fixes.

Nobody is rewarded for it. Promotion packets contain features shipped. I have never seen one that led with lines removed, and I have read a lot of them. This is a fixable problem and fixing it is free.

The risk is asymmetric and personal. If you delete something and you were right, nothing happens. If you were wrong, you caused an incident with your name on it. A rational engineer with a full sprint declines that trade every time. The fix is not courage, it is rollback quality: if restoring a deleted module is a five-minute revert with no data loss, the personal risk collapses and people delete freely.

Nobody knows what depends on it. This is the genuine engineering problem underneath the political ones, and it is where tooling actually helps. Call-graph analysis, runtime usage telemetry on the endpoint for a full business cycle, and a search of the request logs will answer it for most code. A quarter of the time it will not, and that residue is where the real work is.

Instrument before you argueAdd usage telemetry to the suspect path and wait one full business cycle, including month end. Data ends the debate that opinions extend.
Deprecate loudly, in codeA comment saying deprecated stops humans and does nothing to a pattern matcher. A failing lint rule or a runtime warning stops both.
Make the revert boringThe single most effective deletion policy is a rollback path that carries no data loss, because it converts a career risk into a five-minute one.
Budget itA standing target, say net lines removed per quarter in a named area, is crude and works, because it gives someone permission.
Three of these four are about lowering the personal cost of being wrong. That is where the constraint actually is.

Agents propose, humans authorise

There is a division of labour here that is unusually clean, and it is worth stating plainly because most teams have it backwards.

Agents are genuinely excellent at the mechanical half of deletion. Finding every reference, tracing the call graph, identifying which tests exercise only the doomed path, producing the diff, updating the documentation. That work is tedious, error-prone for humans, and squarely in the machine’s competence.

Agents are bad at the other half, which is the judgement: is this actually unused, or is it used once a year by finance in a way that will not appear in a call graph. That question requires knowing things not present in the repository.

So the correct pattern is agents proposing deletions continuously and humans authorising them in batches. Most teams do the reverse: a human occasionally has the idea, and then does the tedious part themselves, badly, at 5pm.

Where this breaks down

Chesterton’s fence is a real argument and I have been fast about it. Code that looks dead frequently encodes a bug fix nobody documented. The person who wrote the odd-looking branch was responding to a real production incident, and removing it reproduces the incident. My telemetry-first advice helps with unused paths and does nothing for weird-looking but load-bearing code.

Some deletions cost more than they save, permanently. Removing a module that nothing calls but that also nothing costs you is pure downside if the investigation takes a week. Dead code has a maintenance cost near zero in a system nobody is generating against. The recurring-cost argument I lean on assumes a high generation rate in the same repository, and not everyone has one.

“Net lines removed” is a metric and metrics get gamed. Set that target and you will get deletions of the safe, irrelevant kind: vendored files, old fixtures, commented-out blocks. The genuinely valuable removals are the scary ones, and no number distinguishes them. I am recommending a target while knowing what targets do.

Deletion is a form of unread change too. An agent-proposed removal of nine hundred lines is exactly the kind of diff that gets approved without being read, because it is large, boring, and feels safe. That is the mechanism by which this advice produces an incident, and I do not have a clean answer beyond reviewing removals of shared code as carefully as additions.

Reversibility is not free either. I have leaned heavily on the boring revert, and reverts stop being boring the moment a schema migration or a data deletion is involved. For anything touching persisted state my whole risk argument weakens considerably.

The takeaway

Generation got cheap and removal did not, so codebases now accrete by default. Every line you keep is not just something to read: it is an exemplar that will be imitated by whatever writes your next feature.

The constraint on deleting is not skill, it is that being wrong is personally expensive and being right is invisible. Fix the reward and fix the revert, and the deletions will happen without a campaign.

If you take one thing into next week: pick the one module you are most confident nobody uses, and instead of arguing about it, instrument it. In a month you will have the answer, and the argument will be over.