There is a common wisdom in software engineering: rewriting a system is always a bad idea, and issues can always be fixed through evolutionary improvements. I no longer think this is universally true.
The idea gained traction from the Netscape story, often cited as proof that rewrites kill products. But the reality is more complicated — Microsoft was aggressively pushing Internet Explorer, Netscape was already struggling, and the rewrite was one factor among many. It also eventually produced Gecko, Mozilla, and Firefox, which still powers one of the two major browser engines today.
The argument against rewriting assumes that architectural complexity reflects real constraints. Often it doesn’t. Many decisions are driven by fashion, cargo culting, and misapplied abstractions adopted as silver bullets. Early mistakes accumulate, workarounds stack on workarounds, and the system becomes increasingly hard to reason about. At some point, incremental repair becomes slower and riskier than starting over.
I saw this firsthand in a small vibecoding experiment. An LLM kept fixing concurrency crashes by adding locks, compounding complexity until a single-threaded app was drowning in synchronization logic and failing unpredictably. Days of debugging with valgrind followed. A clean rewrite with a correct concurrency model would have taken one evening — at least three times faster.
That said, large systems carry real value in tribal knowledge, undocumented edge cases, and implicit constraints invisible in the code. That cost is genuine and shouldn’t be dismissed.
The right takeaway from Netscape isn’t “never rewrite.” It’s that the decision depends on whether the existing system can still be evolved within the limits of human reasoning. Sometimes rewriting is dangerous. Sometimes it’s the only sane path.