~/posts/heavy-doors
Heavy doors
If you’ve ever watched a restaurant kitchen during a dinner rush, there’s a rhythm to it. Multiple chefs working different stations, each one responsible for a different piece of the plate, and the whole thing somehow stays in sync.
Part of that rhythm is discipline, and “clean as you go” is one of the core principles. Not just for health reasons, but when multiple chefs are working together, keeping each workstation clean and ready is the only way anything gets plated. It also feels good to work in a clean space. As developers, we have the same instinct. A clean codebase feels like a clean station.
Clutter kills confidence. We tag it as “technical debt” and spend hours redesigning to remove it, but treating every corner of a codebase like it deserves that same standard is where teams start spending cleanup effort in the wrong places.
A lot of technical debt arguments go sideways because they treat the whole system as one continuous mess, but it usually isn’t. A billing ledger and a holiday promo handler do not deserve the same emotional response. The distinction matters because cleanup is not free. You can spend hours reviewing, testing and migrating. That’s a worthwhile tradeoff at the core of software, but it can be wasteful at the edge.
Some areas are the actual kitchen. Everyone cooks there. Friction accumulates quietly, and once it does every future change gets slower and riskier. Other areas are more like a disposable prep station out back. You use them for uncertain work, keep a heavy door between them and the real kitchen, and tear them down when they’re done.
That heavy door is the actual design work. In practice, that means ensuring the system can keep things contained in the first place. Services talk to each other through narrow, validated contracts with explicit types and clear failure modes. Data that flows through gets checked at the edges, and anything that doesn’t match gets rejected. It’s not glamorous work. Heavy doors usually look boring, and that’s a good sign.
The other thing a heavy door buys you is a clean exit. If something has its own data, its own contract, and nothing else quietly depending on it, removing it is operationally boring. That’s the point. Without that boundary, temporary code has a way of becoming permanent. Shared tables, deep links into the core, other features quietly depending on behavior that was supposed to be short-lived. That’s the debt accruing. Six months later, nobody can remove it because too much grew around it.
You can build these doors with a strict module inside a monolith, a separate service, a serverless function, or a process that talks over a queue. The deployment shape doesn’t matter. What matters is that the door is heavy enough that mess stays where you put it. If the boundary is real, it will. Besides, you’re planning to clean it up anyway 😉