What is legacy code?
The term “legacy code” can describe a variety of code conditions. The exact meaning of the term greatly depends on the developer’s perspective and the current dynamics of the system. But at its core legacy code refers to old code that is no longer being actively developed.
There are principal aspects which classify elements as legacy code:
- The code is no longer being maintained by the original developer
- The code was originally written for operating systems that are no longer supported
- You cannot use automated tests to find bugs in the code
Strategies for code modernization
From our experience, we typically see three options under evaluation: a complete code rewrite; doing nothing; or gradually refactoring / replacing code;
Option #1: Complete code rewrite
Ideally, building a platform using modern design and languages, along with the best patterns and practices your organization has made routine, is by far the optimal strategy. Realistically, however, completely rewriting legacy code rarely happens, and when it does, it’s rarely successful.
Why? Your business and customers cannot pause for months (or years) while you rewrite code. You have to remain operationally competitive and service your customers. Even companies that have experienced plenty of functional issues over the years continue to perform bug fixes, add incremental features and evolve their platforms. They simply cannot afford to risk a major one-time overhaul.
Another consideration here is the scale of a company’s code. We have seen companies grow through acquisition and have as many as 40 different products in their portfolio. Most often, the decision to rewrite or not is made on a product-by-product basis, with strategy teams figuring out how they can best combine code bases, platforms, etc.
Other companies have just one major product, but they likely have it as an integrated suite of sub-products that connect via services. In some cases, individual services require a complete rewrite, but rarely does that become a complete code rewrite. Business simply cannot stop while a rewrite occurs.
For most companies, a major overhaul of legacy code could be disastrous. It would not be economically feasible to hire an entire coding team and build something new in parallel to ongoing operations. When finished, the “big bang” release risks being either underwhelming to users or so new in appearance that users feel intimidated and do not adopt as planned. And of course, the longer a rewrite takes, the higher the risk … and after all, isn’t that one of the biggest weaknesses of the waterfall method?
“Everyone hold on for two years before we know whether this new architecture really works or not. In the meantime, we will spend most of the company’s available budget on this behemoth.” Not so much.
In the end, complete code rewrites are simply not practical and fraught with risk. Exceptions are independently deployable services, interacting with the rest of the ecosystem via API, with a well-established test suite.
Option #2: Do nothing
OK, doing nothing is never an option. At the very least, even a product in maintenance mode will have mission-critical bugs to fix. But beyond that, there are degrees of investment that reflect the company’s product strategy.
We typically see products as either maintained (mentioned earlier) or sustained (minimal new functionality added to keep the customer base). However, in each of these cases, continued work on the existing code base is both more costly and more risky as the language and / or underlying architecture continues to get older and less efficient.
While companies with 30 or 40 products may choose to only maintain certain ones , companies with 1-5 products will always need to do something with their legacy code to stay competitive, relevant and viable.
Option #3: Gradual refactoring or replacement
As we have seen with our many client engagements, gradual refactoring or replacement is the preferred option of the system modernization strategies for legacy code. There’s usually budget already allocated, as companies tend to track 12-15% of revenue for product R&D investment. If you are investing in a product, that means you are refining it, adding new features, fixing bugs, etc.
It would be helpful to address nomenclature at this point. “Modernization” refers to evolving an architecture using a modern, service-based approach. “Re-platforming” refers to moving an ecosystem off of bare iron into a set of virtual machines (e.g., VMWare) or containers (e.g., Docker), leveraging a Platform-as-a-Service (PaaS) solution (e.g., Pivotal Cloud Foundry).
Companies dealing with old software running on a rack in a data center (which no one wants to touch for obvious reasons) will look at re-platforming as a stepping stone to a modernization effort. Re-platforming can help them de-risk their infrastructure and solidify their disaster recovery plans and capabilities.
The decision ultimately comes down to refactoring or replacing components. You are taking an incremental approach, identifying modules that can be carved out and turned into services, and, over time, modernizing your platform.
That assessment basically arrives at a decision by answering a wide range of questions, such as:
- Which components are my company’s primary focus? Think about core domain, intellectual property and main market differentiators.
- Am I just refactoring or replacing them?
- Is the language and platform dated; does it hinder my ability to recruit?
- Are there open-source alternatives or third-party services that I can use?
- Do we want to renovate the UI? If so, when do we want to push people to a new user experience?
- Think about continuous user acceptance testing.
- Be aware of the requirement to run the old UI on the new platform (for slow adopters).
- Are there major bugs that, if fixed, resolve most (if not all) priority issues?
- Which customers do we risk losing without an upgrade? How large is that market segment and how important is that segment?
As you can see, you are not just answering technology questions when you make decisions surrounding refactoring and replacing legacy code. You start with strategic business drivers, then flow down to the supporting code found in architectural components and products.
Give these discussions the space and attention they deserve; they could reveal some misalignments that will help clarify future direction and next steps.