Table of Contents
Key Takeaways
Working on legacy projects is not an easy and most enjoyable task, that’s why most developers would rather work on a greenfield project, than struggling with a few-years old codebase with a lot of legacy, lack of tests and deprecated solutions. Legacy usually brings us many challenges, difficulties, and dilemmas that we usually don’t have to think of when working on greenfield projects. Fortunately, years of experience in dealing with legacy have taught me how to address them effectively and enjoy making things right. In this article, you will find some useful tips and hints on this topic.
In software development, a legacy project is usually described as a project that's old and hard to maintain. The issues are not only connected with outdated codebases but also the tools and scripts, the infrastructure, or unsupported operating systems, hardware, and formats.
Although such a process is costly in terms of developers' time and company’s financial resources, the benefits of structuring the code can be disproportionately higher in the long term. It’s an important investment that will pay off in the future. It's similar to any other debts - a financial debt may allow you to grow and scale your business faster, but if the debt is too large, it can slow down or even ruin your business. So having technological debt is not necessarily a bad thing but it’s important to be aware how “high” it is and how much it’s going to cost you in the long term. Examples of how technical debt may impact business in the bad way are not being able to add new features in a reasonable time, struggling with stability of the system (and thus possibly losing clients), high costs of maintaining the infrastructure because some parts of the code are not optimized, utilize most of the resources and scale poorly.
The main characteristics of the legacy systems are:
Let's be honest, every code at the stage of pushing it to production is already a bit of a legacy due to the high frequency of new updates in the project's dependencies, adding new features that affect previous ones, changing old features to meet business needs and so on. That is why an agile approach is so important here so that the reaction to dynamic changes can be as quick as possible.
This step may seem time-consuming for you but in fact, it will save you more time in the long term. Reviewing the documentation with the original requirements will help you in understanding where the code came from and make changes to it later on. Having all the necessary information will also prevent you from making any modifications to the code that would cause undesirable actions.
Without conducting tests of the code, you're not able to know all of its capabilities. Testing your code reveals more possible scenarios of how your code should actually work and gives you a better understanding of possible flaws and edge cases of the functionality you intend to implement. Unfortunately, legacy projects are often characterized by the fact that the code used in them is insufficiently tested, and changing it usually requires introducing a regression somewhere.
I have two messages for you - a good and a bad one. The bad news is that it’s good to start with reviewing tests suits and code coverage to make sure you are safe with changing the code without breaking things and keep the most important functionalities running. If there are no tests at all, it’s good to start with setting up a testing environment, select the right tools and write some proper tests. The good news is that you can make it a little easier for yourself. According to some tips included in Working Effectively with Legacy Code written by Michael Feathers, you should try some of these methods:
In a broader perspective, the solution to problems with legacy projects is to gradually break down a large system (monolith) into individual, less coupled modules and layers within the same application (modular monolith) or into microservices if it’s really neccessary. The division of the code within a modular monolith gives us the following benefits:
When working with legacy code, you'll probably struggle with refactoring some really poorly implemented parts that you can’t really understand. In such cases it might be actually better to start from the very beginning instead of trying to understand and improve already existing code - in some cases it will save you both time and nerves. Even when it takes you more time to create new code, you may come up with a much better solution than you would end up with by refactoring it, which will ultimately also be beneficial. Just remember to not introduce many new approaches and patterns - remember it’s still easier to maintain the code that follows certain patterns used in the project than experimenting with several different approaches in different parts of the application, just because you are used to something or just because, in this particular case, it would be nice to try out a completely new approach.
To make things easier for yourself and others in the future, make sure that the new code you write is clean. This will help avoid problematic situations in the future or improve bug fixes. You are not responsible for the code you inherited, but you can ensure the quality of the new code you write by keeping it clean.
When working on re-engineering legacy software, it's good to get help from more experienced experts who know the codebase better than you do. This will speed up your work and prevent yourself from wasting your time guessing.
Although working with legacy projects is demanding, challenging and usually it is not as attractive as a greenfield project with the latest tech stack but in the end it may also give a lot of satisfaction, new knowledge and it’s not as scary as many developers imagine. It’s good to stick with good practices and rules and listen to the wise advice of colleagues more experienced than us, and above all - not to give up!
Looking for a partner who not only understands your challenges but anticipates your future needs? Get in touch, and let’s build something extraordinary in the world of digital health.