The Birth of Legacy Software
There is a need to change the existing system behavior to accomplish new functionality.
The software engineer looking at the task realizes that the existing design isn’t well suited to the change needed. They suggest design changes and refactoring, as part of implementing the desired functionality.
Their peers review the work, and they realize that the changes being proposed are risky. Because no one in the team has a great understanding of the system, they worry that any refactoring will introduce unintended bugs. They have been bitten by this in the past, and they are determined not to make the same mistake again.
They identify a way to avoid making significant changes. “Right here, in method Foo in class Bar, just add another if-check, a service call and/or a dependency to class Baz."
Separation of concerns gives way to an intersection [ed: I think this is more like a union] of concerns.
The above process repeats itself every time a change needs to be made. With each iteration, the design becomes a little more muddied, the system becomes a little harder to understand, and behaves in slightly more unpredictable ways. All of which makes the team even more risk averse and determined to avoid unnecessary changes. Which in turn accelerates the rot even further.
And any time someone leaves the team and is replaced by a newcomer, this acceleration kicks into overdrive.
Pretty soon you have a full fledged legacy system that everyone loves to complain about and no one wants to meddle with. The code base is no longer a living system, but a museum artifact. It is meant to be gazed on with wonder and mystery, but certainly not touched.
The Origins of Legacy
Legacy software is any software where people are afraid to make changes.
The two most common reasons for change aversion:
- Complexity
- Test coverage holes
In order to truly figure out how good their test suite is, there is one simple question you can ask: how much time do you spend on manual testing?
there are a lot of teams that claim to be very disciplined about writing tests, but also spend significant amounts of time on manual testing.
Often because their tests are focused only on unit testing, and not on the emergent functionality that you can cover in end-to-end testing.
Breaking Out Of The Doom Loop
The more common and ideal way to break the doom loop is by investing heavily on automated testing, incremental deployments, and automated monitoring and alerting of production errors.
Finding The Will
If your team is already well inside the doom loop, it can be very hard to break out of it. The solutions discussed above don’t deliver any business value in and of themselves. Hence the tendency to throw them on the back burner. It’s hard to justify spending significant time on testing, monitoring, and deployment enhancements, for a functioning legacy system, when the marketing team is insistent on releasing the next killer feature that will delight users and win over the competition.
Breaking out of the doom loop can certainly be done with concerted effort and investment. But that is something most maintenance teams are seldom given. Hence why they invariably spiral further and further… until things get so bad that everyone decides to junk the whole thing and rewrite it from scratch. At which point, one legacy system is retired and the next legacy system is born.