As software consultants or software engineers, we spend a lot of time dealing with legacy code and either enhancing it for a new business use case or refactoring the original code to make it fit for a new purpose. Only some things are fantastic green field projects where you can write a whole system from scratch.
Analyse the software landscape
Much like how you would assess the requirements for a new task, this particular analysis needs to focus on the system as a whole. This means the codebase, its tests, deployment of the code and any downstream services that rely on the codebase you're working on. A couple of great starting questions are:
- What is the code supposed to do? What does it actually do? How well does the code achieve its goals?
- How well is the current codebase documented and tested?
- How often is the codebase updated?
These questions will guide you to where the most pressing issues are. For example, if the code is updated frequently as it's a monorepo or core service, what's the deployment process like? There are likely easy improvements to make in this area to allow for a faster and higher quality of delivery.
There are lots of tools to help with code analysis and identify code coverage/code smells in order to get a more solid idea of the code quality and complexity you're dealing with. These tools should be used in tandem with speaking to the engineers working on the codebase as well as stakeholders to see where things are slowing the team down, what the business logic should be and where priorities lie for the business with the particular services you're reviewing.
Improvements to maintainability and readability of the codebase are great, but not if it comes with a negative effect on the system's performance. So always keep an eye out for the metrics in which the system is measured, e.g. response times, amount of users signing up, time taken to acquire new users, etc.
Devise a plan of action
Once you've analysed the codebase and gotten enough information to start with, it's time to create a plan of action. How will you deal with this codebase? What are the key areas for improvement?
Depending on the scope and budget of the project, various methods and approaches can be used. Two common approaches are to create a facade layer in front of the legacy code which simplifies interaction with the legacy code and only exposes key functionality. The facade layer will be built from the ground up, so you can follow a simple design pattern to keep things extensible and maintainable going forward.
Another common approach is to gradually replace parts of the legacy code with new services, keeping the old code living with the new code until fully replaced. The new services will be your opportunity to architect good software and also will be built from the ground up, you can use a design pattern if you choose to do so.
Opt for a design pattern
Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customise to solve a recurring design problem in your code.
If it fits your use case, or you're goal is to simply improve the current legacy code base, pick a software design pattern that works for the requirements you have. A good guideline for choosing a design pattern is one which makes the system easier to test.
There are tonnes of design patterns out there in the wild, so considering a few factors such as trade-offs, programming language/framework being used and the level of abstraction will help inform your decision.
See the following link to a list of common design patterns - https://refactoring.guru/design-patterns/catalog.
Test, validate and review
The last thing you need is to test, validate and then review the changes made. You need to ensure the software you've created is fit for purpose and works the way you think it does. Beyond unit testing, adding a whole heap of integration and regression tests around your services and code base will help ensure that no bugs have been introduced to other parts of the system which you haven't touched, as well as verifying things still work smoothly.
Documenting your code and its changes are key here in order to provide visibility into your codebase without someone having to read each line of code to understand your system. Once you get to this stage, it's a good point to take a step back and examine the changes you've made and if you need to reiterate in order to improve the system and codebase.
Conclusion and takeaways
As you can probably tell, there isn't a "one size fits all" approach to dealing with legacy code. The key takeaways I'd like to provide are:
Cater to your use case. Mapping through the system to find where your specific pain points are will help not only other engineers but also project managers to buy into the approach you've put forward.
Ensure you have methods to measure system performance and key business metrics of your refactor.
Keep maintainability at the forefront of your mind. You'll most likely find parts of the system undocumented, in such cases creating a README for that service/component or a runbook will help other engineers when maintaining or making future code changes.
Something that should always be considered is keeping an eye out for scope creep. It's great making a system easily extensible, but there comes additional time and complexity in doing this if there are no plans in place to actually use your extensible code in the near future.
Any thoughts you might have, feel free to comment & dicuss on this post!
Top comments (0)