Afraid of legacy code? Don't be!!!
Most successful product companies are confronted with the problem of legacy code.
What is a legacy code?
- A code which is in production for several years.
- A super-complex, hard to understand code base, written by different set of developers.
- Outdated Technology stack.
But the most hurting reality is:
Lack of confidence in the code due to zero or poor test coverage.
Due to this reality, developers are often scared to touch it. They have very little confidence that "their code change wouldn't break the existing application in production."
Recently at IDeaS, we came across such situation, where we needed to enhance one of our products containing legacy code. We started looking into the code and soon figured out that it was developed in 2007, hardly ever touched (& still working in production :)). The original team, which has worked on this product, could not be traced anymore.
As this product has expanded to attract new customers, we had to change it significantly in order to support new customer's specifications. We had to make sure that the product was backward compatible and supported the earlier specifications, while we enhance the new specification.
One simple option was to COPY PASTE every single method which needs to be modified and use an if-statement to decide which method to call. This certainly seems like an easy method, since the chances of breaking existing code is very little.
Today we all know this is a BAD option!!!
Instead, our team decided to refactor the existing code to support plug-and-play approach for different specification. But before we started refactoring code, we had to build a safety net of tests around the existing code.
How do we put the safety net? Ideal way would be to implement the Test Pyramid first. But, that would have taken significant time to be ready with the pyramid before we start touching the legacy code. And obvious, we would have missed the business goals.
What do we do?
Instead of building the entire test pyramid, we decided to attack different layers of the test pyramid, one at a time. Along the way, we followed the following approach:
- Re-structuring the Project code-base
- Establishing a baseline database: After taking a dump from the production database, we cleared out surplus data from the DB and setup a seed database with automed scripts
- Creating/fixing the build script
- Setting up an auto DB deploy tool and integrating it with build scripts
- Set up basic CI pipeline
- Write a few work-flow tests to capture the system's flow from user's point of view
- Find the inception point in the code from where we can exercise the code
- Restify the application at the inception point (one service at a time)
- Setup authorization for production and test environment
- Build minimal test-data set for different environment
- Create a few work-flow tests via the inception point (Test itself should not be coupled with the underlying database or implementation level components)
- Write business logic acceptance test to capture various complicated business rules
- Test drive the new enhancement or bug fixes
- Every time we touch legacy code, refactor the code and improve test coverage at unit level
This really helped us test driven the new code and implement all the layers of the test pyramid.
If you've a similar situation, join us, as we share our experience on how to confront legacy code.