Practice
The rewrite is almost never the answer
The instinct to start over is usually a reaction to not understanding the current system. That feeling goes away. The two quarters do not come back.
01Why it always looks appealing
Reading code is harder than writing it. Your own code, written today, with today's understanding of the problem, feels obvious. The existing code was written across two years by four people responding to constraints that were never written down, and much of what looks like nonsense is a bug fix you have not encountered yet.
Every weird conditional in old code is a scar. You cannot tell which ones are load-bearing by looking at them.
02The part that gets left out of the estimate
A rewrite estimate covers building the features you know about. It does not cover the years of accumulated edge cases, the data migration, the period where two systems run in parallel and every change has to be made twice, or the fact that the old system keeps shipping features while the new one does not.
- Feature parity is a moving target, because the old system does not stop.
- The undocumented behaviour is the product. Somebody depends on it, and you will find out who by breaking it.
- Trust is spent up front and repaid only at the end, which is the worst possible shape for a project.
03What to do instead
Almost every problem that motivates a rewrite is bounded and addressable in place. Slow? Profile it — the answer is usually three specific things, not the architecture. Unmaintainable? Add a test harness around the worst module and refactor behind it. Wrong abstraction? Strangle it: put a new interface in front, move call sites one at a time, delete the old path when nothing points at it.
I have taken the incremental path on four systems and the rewrite path on one. The rewrite was the right call and it still cost more than the estimate by a factor of about two. That ratio seems to be a law of nature.