One of several projects I’m currently involved with is the re-write of an 8 year-old system that is roughly 8 years past its useful life *rim-shot*. In all seriousness, NaughtySystem, as I’ll refer to it, has been a PITA for as long as I can remember. It’s unnecessarily complex, fragile, poorly designed and lacks tests or documentation. This is all the more frustrating given the very simple problem domain NaughtySystem was built to solve.
Rewrites are really, really hard
The single worst strategic mistake that any software company can make: rewrite the code from scratch.
– Joel Spolsky
Given my severe disdain for NaughtySystem, you’d think I would have been campaigning to re-write this thing long ago. But I rarely advocate for system re-writes in all but the most extreme cases. Why? Because…
It’s more work than you think
The effort required to create a new system that is functionally equivalent to its predecessor is typically vastly underestimated. By their nature, legacy systems have been around a very long time; after all, that’s usually why we seek to modernize them in the first place. But their longevity also means that these systems contain a tremendous amount of functionality. For example, it may have taken up to 2 developer years to achieve the feature density found in a 10-year old application. Keep in mind that you would need to make a similar development investment in your replacement system before being able to deliver a single new feature to your users. That sobering fact is frequently not considered when estimating or prioritizing replacement systems.
Legacy systems possess another characteristic that complicates a re-write: they are poorly documented. With the original developers long-gone, and no automated testing suite to act as living documentation, there are likely to be a great many hidden features lurking in these apps, further eroding the accuracy of our estimates.
Incremental change is off the table
The problem with a complete system re-write is that you can’t retire the old system until you have complete feature parity in the new system. After all, the legacy system, regardless of the brittleness of its implementation, still delivers some value to the business in its current form. So if you can’t release until you have a full replacement system, incremental releases are out the window and you’re now dangerously close to waterfall territory and all its inherent disadvantages.
Targeted re-writes are better
For the aforementioned reasons, I favor gradual modernization of legacy systems; this strikes the right balance between paying down technical debt, continuing to deliver value and minimizing projects risks. A gradual approach can take multiple forms, but we’ve found the most success by targeting a specific horizontal or vertical slices in the system. For example, re-engineering an application’s buggy/slow persistence layer (horizontal) may provide the greatest short-term relief. Alternatively, modernizing the shopping basket functionality in an e-commerce app (vertical) may prove most beneficial, particularly if that’s where the business intends to make frequent feature changes.
Of course, this is just a general rule of thumb. In some circumstances, a system may be too far-gone to be reasonably or economically upgraded in a piecemeal fashion. In these cases, a re-write may be the right answer. Which brings me back to NaughtySystem.
One of the major drawbacks of a legacy code base is that adding new features is inordinately expensive….but it is still possible. For systems that change infrequently, such as NaughtySystem, it doesn’t make economic sense to incur the large upfront costs of a re-platform, targeted or otherwise. The timeline to re-coup such an investment is likely to be a very long time.
Recently, however, the calculus changed. After years of not being a high priority, product has signaled a desire to make fundamental changes to NaughtySystem Suddenly, the economics around a re-write made a lot more sense; the cost and risks associated with making substantial changes to the legacy system swung the pendulum in the other direction. Mind you, the same risks outlined above still apply. The difference now is that the risks/costs of attempting to extend NaughtySystem outweigh the risks/costs of a re-write.
In subsequent posts, I’ll outline the technical challenges we faced during the re-write.