How to Work on Legacy Systems

The first system I worked on, at my first job out of college, was a  legacy PHP application. The code was like a piled up plate of spaghetti.  Although it was an application used by tens of thousands internally, attempts at rewrites had failed. It was a critical system but there was on strategy on a better path forward.

This was how my career started, not with an exciting cutting edge project, but rather a mandate to keep the lights on for an application that was long past its shelf life.

For many developers, this is your day to day reality. You won’t always be fortunate to work on greenfield software systems where you craft the architecture from ground up. Rather, you will spend a lot time working on, or rather wrestling with, legacy systems. System that have been around longer than their original developers. These are applications that very few people know how or why the code still works. Most are afraid to touch them. Previous attempts at improving or rearchitecting such apps result in long projects that eventually get killed. And so, now devs only prefer to surgical add more features and also more duct tapes to keep it all stable.

Working on such system can feel disheartening. But it doesn’t have to be. Owning a legacy system is an opportunity to demonstrate strategic thinking and problem solving skills that can lead to fulfillment and career growth. This is a form of growth mindset.

Other than mindset, there are two other  obstacles that prevent developers from improving things in the legacy system. A fear of making changes is one of them. And the absence of background knowledge on the existing architecture is the other.

Both obstacles are related. The fear of making changes stem from the lack of in-depth understanding of the architecture and the reasons behind the technical choices. This can lead to any changes towards improvement to unexpectedly break existing functionality. And so before any technical strategies can be considered, you need to make it safer to work on the codebase and increase the confidence that things won’t break.

That said, why make any changes to the legacy system at all? Why not leave it as is and build a whole new system from the ground up? I’ll leave the discussion around approaches and tradeoffs around this question for a different post. Here, we will assume, as is the case for many legacy systems that there is a need to continue to maintain and add features to it. A full rewrite and rearchitecture is often left off the table due to time and cost considerations. However, the work that you do to improve the legacy system in-place can inform and guide the decision on a larger rearchitecture in the future.

Here’s a simple framework on how you can turn your legacy application into a great well crafted system.

1. Speed safely:  Introduce and improve safety harnesses around the system

Step one, ensure that system up time and bug count isn’t negatively effected as you begin to make improvements. The better the safety harnesses you have in place, the bolder the changes you can afford to make.

There are 3 specific areas to consider: test coverage, monitoring and alerting.

The legacy system likely has very little tests in place. Adding unit tests would probably require a fair amount of refactoring. Investing in integration tests is often more practical. For example, you can add test coverage for key REST endpoints, or API interfaces for core modules of the system. Adding monitoring and alerting to the system will also further ensure that any runtime issues are caught before users and customers see them.

Take the time to invest in the stability of the system to give yourself the confidence to eventually make  deep restructuring changes.

2. Measure: Establish baseline metrics

Invest in instrumentation to get a sense of the baseline performance metrics of the system. For example if it’s a frontend application, track the page load time. For backend systems, get a sense of the latency distribution for various sizes of traffic. There are two goals for establishing these metrics. The first is to ensure you don’t degrade them as you refactor and rearchitect the system . And the second goal is to have a clear measure of the improvements that you want to aim for. Improvements to the metrics that you deliver will help you win budget for further investment in the system.

3. Rediscover: Talk to your users to revisit the value the system delivers

Legacy systems often have a lot of old features that are maintained but not used. You could get a general sense of this by setting up some tracking to monitor usage. Another option is to talk directly to the users. I highly encourage technical leads and engineers to talk to users often. It’s a great way to learn how your work delivers value and where you can find rooms for improvements. You will likely find users to be pleasantly surprise at the attention the system is getting. If there are any low-hanging improvements you can make, then carve some time to quickly deliver them. This is a great way to build excitement and draw attention to the system.

4. Take ownership: Immerse yourself into the depths of the system to make it your own

When you are air dropped into a legacy system for the first time, the landscape feels foreign. You are dealing with code who’s owners are likely not around. Every time you encounter a new area of the code base, struggling to make sense of why things were done the way they were, you quietly curse the previous developers. In order to make progress and avoid the feeling of dread, it’s important to make the code your own. A quick tip/example on one of the  way to do this is to update or add comments as you read through new sections. Feel free to move around sections of code and organize files according to a structure that makes better sense to you. Document as you go and share new finding with your team or organization.

Overall this will lead to better mental health when dealing with this code base.

5. Take action: Develop and execute a bold technical strategy

All four of the above steps taken together will help you develop a broader technical strategy. A technical strategy is a clear direction of how the system can be architected to be more performant, more pleasing to work with, and deliver more value to end users. This is a direction you can move towards with every iteration of improvement you make to the system. My recommendation is to develop a design document and or a prototype to demonstrate what a different architecture can look like. In some cases, you will find that the prototype or design becomes the basis for a complete new rearchitecture or rewrite of the system.

To be clear, these 5 steps don’t have to be executed in sequence. Rather they can run in parallel, each supporting the other. For example building in more integration tests will help with taking deeper ownership of the code base etc.

Legacy doesn’t have to be a “bad” word. There are some amazing opportunities and value that can be found in these systems that have long been neglected by people rushing towards the next shiny project.

If you find yourself working on a legacy system, take pride and ownership in your work and you will find enjoyment in it as well.

Leave a Reply