6 min read

In my 10+ years of experience working as a software developer, I have spent more time working on legacy systems than on greenfield projects. What is legacy code and why do companies and teams invest so much to rewrite the code which is already working?

 What is legacy code?

 When we talk about legacy code, we think of old code written in old technology. But in reality, any code that is difficult to work with is legacy. If you are working on a code base that is difficult to understand, and making a small change takes a long time, then the code is legacy code.

“Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.”

– Michael C. Feathers, Working Effectively with Legacy Code

In today’s competitive world, companies want the software to be able to change quickly. However, working with legacy code is always slow because of the fear of introducing bugs, since there is no way to verify that the system is working as expected.

Software modernization

In my experience, the most common strategy to deal with legacy code is to replace it with clean code. If we keep adding more code to a legacy system without cleaning it up, we are just increasing to the technical debt. The process of rewriting legacy code to a modern technology is known as software modernization.

Rewriting an existing application is quite different from writing a new app. In a greenfield app, we can start with a minimal viable product and then evolve our system based on user feedback. This image nicely explains the development cycle of a new product, so that you solve a user problem and then iteratively make it better.

Unfortunately, when rewriting an existing app, we cannot follow the same approach because the end users already have the full-fledged solution. It will be like asking a user to use a skateboard as a replacement for a car.

Because of this reason, some teams decide to write a new system in parallel to the legacy system. This means the legacy system is still live and in operation while the team is working on a new system to replace it. I have seen this approach work in a few small projects where the legacy system was quite stable and not in active development. However, in case of a large system that was developed over many years and is still changing based on new requirements, there is a lot of code to rewrite, which in most cases is not well documented and the requirements are not clear. All of this results in projects getting delayed, and business is not getting anything in return of their investments, and eventually projects get cancelled after companies have already invested a lot of time and money into it.

Strategy to manage legacy systems

Just like a new application, it is not a great idea to follow a big bang approach to tackle a legacy system. Here is the strategy to convert large legacy codebase to clean code.

  1. Divide and rule – Divide the system into different domains or modules and start rewriting one module at the time. The smaller the modules, the easier and quicker they will be to replace. The business will see return for their investment, users will get a better system, and along the way the development team can learn how to make the whole process smoother and better for the next module rewrite.
  1. Integrate with the existing system – It is worth investigating on how to integrate a module written in the new technology with the existing system. Start with a walking skeleton, that is, a very small functionality written in the new technology, and integrate it with the existing system. It is better to link all of the main architectural components as soon as possible instead of leaving the integration part for later.
  1. Test covering – While dealing with legacy code, there is a very good chance that the requirements are not very clear or well documented. We want the new module to work the same way as the existing legacy system. The best way to achieve this is by writing tests. Start with writing tests to run against the existing code and verify its behavior. Then the same tests can be reused and run against the new module to make sure it works the same way as the existing code.
  1. Faster feedback cycle – Even if we break down the system into smaller modules, we want to keep the feedback loop as small as possible. Use a continuous delivery approach to release software faster and more frequently to the users. Automate the release and deployment process and keep the development and testing environment in production—try to avoid any last-minute issues on deployment.
  1. Build a better system not a replica – We are putting all of this effort in rewriting the system, in order to think about making it better. Improve user experience by making the interface intuitive and simplify workflow, save time and effort by removing features that are not required anymore, and add the features that the users always wanted.
  1. Work in feature/domain teams – One key difference I have noticed between successful and failed/delayed projects is the team structure. Projects with cross functional teams have higher chances of meeting their goals, because everyone required to deliver the project is part of the same team. On the other hand, dividing teams based on technology like frontends, backends, and operations team can slow down the delivery process due to the lack of communication, and because each team has different priorities.

Hopefully this has given you a good starting point for approaching legacy code. It can certainly be a bit of a challenge, and it demands that you think about the specifics of a given situation and ask yourself what’s at stake and what’s really important. 

If you’re interested in learning more about dealing with legacy code, check out this article.

About the author

 Amit Kothari is a full-stack software developer based in Melbourne, Australia. He has 10+ years of experience in designing and implementing software mainly in Java/JEE. His recent experience is in building web applications using JavaScript frameworks like React and AngularJS and backend micro services/ REST API in Java. He is passionate about lean software development and continuous delivery.

LEAVE A REPLY

Please enter your comment!
Please enter your name here