The Rewrite Is a Knowledge Bet
The urge to rewrite is strongest exactly when understanding is weakest. You open an unfamiliar module, it’s tangled, the naming is inconsistent, there are branches whose purpose you can’t reconstruct — and the immediate feeling is that this would be faster to redo than to comprehend. Sometimes that’s true. But it’s worth noticing what the feeling is actually reporting: not that the code is bad, but that you don’t have a model of it. Those are different observations, and the second one is about you rather than about the code.
What makes this dangerous is that the mess is often where the knowledge lives. A system that has run in production for years has been shaped by everything that went wrong: the special case for the one client whose data is malformed, the retry that exists because an upstream service is flaky at a particular hour, the seemingly redundant validation that caught a real corruption once. Each of those made the code uglier and made it correct. A clean rewrite reproduces the intended behavior beautifully and drops the accumulated corrections, because they were never written down as requirements — they only exist as the shape of the code. Then, over the following year, you rediscover them one incident at a time.
This is the same problem as the why that evaporates, just at a larger scale. When the reasons behind the strange parts are lost, you can’t distinguish essential complexity from vestigial complexity, and a rewrite is what happens when someone decides the whole thing is vestigial. That’s a bet: you’re wagering that your fresh understanding of the problem is more complete than the accumulated understanding embedded in code you couldn’t fully read. Occasionally that bet is right, when the original was genuinely built on a wrong model and the domain is now well understood. Often it’s wrong, and the tell is that you’re making it about a system you couldn’t explain in detail an hour ago.
The alternative isn’t never rewriting; it’s earning the right to. Build the model first, by the means that actually build models — change something small and watch what breaks, trace the odd branch back to the commit that introduced it, ask the person who’s been there. Very often that process changes the plan: half the mess turns out to be load-bearing and the other half yields to a targeted refactor that’s a tenth the size and risk of the rewrite. And if you still conclude that a rewrite is right, you now do it knowing what the old thing knew, which is the only condition under which a rewrite reliably comes out ahead.
That’s the thread underneath all of this: a codebase is a store of knowledge, and most of it is undocumented. The why disappears faster than the code, understanding gets built by doing rather than by reading, and so the parts you don’t understand are exactly the parts most likely to be holding something you’d regret discarding. Treat unfamiliar strangeness as unexplained rather than as wrong. The cost of investigating first is real but bounded; the cost of replacing knowledge you didn’t know was there is neither.