Almost everything we’re taught about good design is about addition. Make it easy to add a feature, a provider, a payment type, a region. Extensibility, the open/closed principle, plug-in this and strategy-pattern that — it all points one way: build so the system can grow.
The business pulls the same way. Roadmaps are lists of things to add; nobody puts removals on a roadmap. I’ve never once been handed a quarter’s plan whose goal was a smaller system.
So almost nobody designs for the opposite. How easy is it to take something out? Over the life of a real system, that’s exactly where the pain lives.
Why deletion is the real test
Adding is optimistic work. Everyone’s keen, the requirements are fresh, the code is new. Removal is the unglamorous job that lands later — when a feature flopped, a client left, a third party got acquired, or a “temporary” integration outlived everyone who understood it. I’ve spent a fair slice of thirty years on the removal end of that, much of it in insurance and point-of-sale systems where almost nothing is ever truly switched off, and it’s taught me more about how a system was really built than any amount of reading the original design docs.
That’s usually when the truth comes out. I’ve walked into all three of these more times than I’d care to admit:
- A feature that couldn’t be removed because half a dozen other things quietly reached into its tables.
- A vendor integration woven so far through the codebase that ripping it out was a quarter’s work — so instead it sat there, dead, still costing money and attention.
- A flag that was meant to be temporary and had long since become load-bearing.
A system that’s easy to add to but impossible to delete from only ever accretes. It gets bigger, never smaller, and every year it’s a little harder to reason about — and slower to fix or extend, because every change has to pick its way around the things nobody dares remove. Code you can’t delete is code you’re stuck with forever.
Designing for removability
Optimising for deletion is really just coupling discipline pointed at a more demanding test. “Could I cleanly cut this out?” is a far harder question than “could I add to this?”, and over the years it’s become one of the first things I ask of a design. A few of the things I’ve learned to look for:
- Make ownership obvious. A feature that owns its own data and its own surface can be lifted out as a whole vertical slice; one whose data is smeared across shared tables that everyone reads cannot. This is the domain-design question wearing a different hat — the aggregate is the unit you can delete atomically, the bounded context is the seam you cut along — but that’s a post in its own right.
- Depend on interfaces, not implementations — not for the usual reasons, but because an interface is a clean line to cut along; whatever sits behind it can go without chasing callers all over the codebase. For anything external — a payment provider, say — that line has a name: an anti-corruption layer. The usual objection, “we’re never going to change our payment vendor”, never survives contact with reality: I’ve changed mine three times at one company, and watched a finance provider go from external to in-house and back out to external again. The thing you’re certain will never move is usually the one that moves most.
- Keep the blast radius small. The real cost of removal is everything that breaks when a thing goes. Low coupling isn’t an aesthetic preference; it’s what makes deletion survivable.
- Treat “temporary” as a promise, not a label. Anything meant to be short-lived should be built so it can actually leave — behind a flag, behind a boundary — with a note on how to remove it written down while someone still remembers.
A useful habit
When I’m reviewing a design, one of the more revealing questions I can ask is: “If we had to turn this off next year, what would it take?” If the answer is “a small, well-understood change,” the boundaries are probably right. If it’s a sharp intake of breath and a long silence, we’ve designed something we’re going to be stuck with.
The systems that age well aren’t the ones that were easiest to build. They’re the ones where the bad ideas could be taken back out cheaply — where the architecture made room for being wrong.