Back to Articles

Make legacy code great again

Written by Ben Gurney, published on

Legacy code is a problem that many businesses face. Changes become harder to make without things breaking, nobody really knows how anything works anymore and the customer experience starts to take a major hit. As developers, we love to scapegoat it, complain about it and yearn to replace it. But what if we didn’t?

How does code become legacy?

Before jumping into things, we should probably explore what we might mean by legacy code. As developers, we love to throw this term around, but in my experience it generally boils down to one of two things:

  • Code we don’t like
  • Code we don’t understand

Let’s take a look at these two statements in a bit more detail.

Code we don’t like

This is pretty subjective so it really comes down to the individual to justify what they don’t like and why.

Some things we might not like as developers:

  • It doesn’t use a framework / uses an outdated framework
  • It has no tests
  • It doesn’t use a dependency manager / uses outdated packages
  • It doesn’t follow basic principals / design patterns

If we look at why we don’t like these things, we’ll probably find that it’s for a range of perfectly valid reasons, such as:

  • Security vulnerabilities
  • Low productivity
  • Difficult to upgrade
  • Decreased developer experience

But why is the code like this in the first place? Well, personal preferences aside, it’s probably due to a lack of code maintenance over a prolonged period of time. Yep, the code might not have changed, but everything around it has!

Code we don’t understand

Sometimes code can be a tangled mess and extremely challenging to follow. If we don’t understand how or why something works, we understandably become reluctant to change anything out of fear of breaking it. But why would somebody (possibly even us) write code that we can’t understand?

Well, because it’s hard to write code that people can understand.

As humans, we’re constantly building up different mental models to help us understand the world around us. As developers, we write code based on our current mental models. As we learn more about the domain we’re working in, or requirements change in some way, our mental models change too. If we do not refactor our code to reflect these changes, over time the models in the code break down and it becomes harder to understand, especially for other people.

So there we have it! Two reasons why we might consider something legacy code, and how we might end up there. Now we just need to figure out what to do with it…

Mindset

Before we start, let’s make sure we’re fully bought into the approach we’re going to take. It’s not going to be an easy road to navigate, so we need to be clear in our own minds that we are heading in the right direction.

First and foremost, we need to accept that there is no “throw it away and start again” option here. Read that again. No matter how much we think we could do it quicker, better, or cheaper, no matter how much the code might suck, the fact is, legacy code delivers massive business value. We have to remember it’s this legacy code that is (and has been for a while) serving customers and generating money for the business. We can’t just throw that away.

What about rebuilding it all? That could work! Perhaps. But to try and do this all in one go would be very risky in my experience. Unknown dependencies, business logic blackholes, an established user experience. Rocking the boat too much for any one of these things could have a detrimental impact on the business, let alone rocking all of them at once. And that’s before we address the practicalities of time. When would we do this greenfield project with all the other work we have to do to keep up with business demands?

Now, accepting that we can’t just throw it away doesn’t mean that we have to like it, or how it works, or even how it looks – we’re not lowering our standards here. It doesn’t mean there isn’t a problem to fix, because there is! But we must adopt an “improve” mindset rather than a “replace” mindset before we start. Progress over perfection is the key here.

As Chinese philosopher Lao Tzu famously said, “The journey of a thousand miles begins with a single step”. So let’s take our first step.

Upgrading

Let’s start by upgrading what we can.

It’s easy to feel trapped by legacy code, uncertain of what path to take or where to start – it’s overwhelming. And it’s often this feeling of being overwhelmed that makes us want to double back and start again with a fresh. Upgrading what we can is a really great way to take that first step.

This might be the server, PHP version, database version, framework or even specific libraries. Our goal right now is to forget about the implementation details of the code and focus purely on the environment in which it is running. Often this will require some sort of dependency mapping exercise to understand what can be upgraded, how far and in what order. But start small and gradually work your way forward. What we’re doing here is removing our glass ceiling and laying the foundations for a better future!

To keep us motivated we’ll focus on what we’re unlocking by doing this work; the ability to install a proper framework, use our favourite libraries and even remove some of those security vulnerabilities. And whilst we’re at it, let’s agree an upgrade schedule with our team so that we can keep things up-to-date moving forwards.

Hybrid development

Once we’re able to use some better tools, we can essentially setup a hybrid development environment for ourselves. This is where our existing code can run side-by-side, or even inside our new application code.

The main benefit here is that we will be able to use our newer technology stack to build our new features (much more fun!), whilst incrementally upgrading and replacing legacy parts of the code.

It’s not always straight-forward to get both sides of the application working nicely together, but it is possible and just takes a bit of code massaging. Some key areas to focus on are:

  • Sessions
    Upgrading legacy code to use the same session storage as the new application code will ensure everything stays in sync.
  • Routes and authentication
    Placing old application routes behind the new authentication system will immediately improve the security of the application as well as providing a central point to manage the routing between legacy features and new features.
  • Tests
    Writing some tests for the most important features of the legacy code will not only help us understand them better, but also give us test cases for when we come to redevelop them later.
  • Styles
    Creating a common header and footer that’s shared between legacy and new pages will give the application a unified feel, even if the inner page styling is slightly different. (Sometimes using multiple CSS frameworks on one page can cause styling issues due to loose selectors, but I find this can be overcome with CSS scoping).

It can be useful to document specific strategies that have been employed during this process too, whether for other developers, or your future self, as it is likely to require some creative solutions!

By the way, we’re planning to write some more articles that focus specifically on doing this with technologies we work with. Something along the lines of “moving to Tailwind CSS”, “moving away from jQuery” and “getting your old PHP application to run within Laravel”. So keep an eye out for those!

In conclusion, a hybrid development environment may take some time and effort to set up, but it's a great way to move an application forward. And the benefits of doing it? Well, in addition to increased productivity, security, stability and performance, we get to use all our favourite tools again!

I personally find the whole process quite liberating and once I have an application setup this way, I no longer see the legacy code as something that sucks all the joy from my work, but as something to learn from and build upon in the next iteration.

Need some help?

We’ve been uplifting legacy codebases for many years and our in-depth knowledge of the different web technologies used in the past 20 years puts us in a great position to help you. So, if you feel like you could do with some extra help in this area, schedule a 20 minute call with us!

Feel free to connect with me on LinkedIn if you'd like to discuss this further or ask me any questions.

About the author

Hey, I'm Ben - a digital consultant / full-stack web developer with over 20 years of experience.

My ability to blend technical knowledge with business acumen allows me to develop intelligent and sustainable solutions that deliver business value, increase productivity and reduce costs.

I've worked across a number of industries including telecoms, education and energy management and am always keen to bring my experience to new industries and business types.

My preferred technology stack is PHP/Laravel, Livewire, Alpine.js and Tailwind CSS, but I'm also well positioned to help businesses migrate from older technologies that may be holding them back.

I work well in teams, whether coaching, collaborating or leading and understand the importance of stakeholder engagement, communication and planning.

Your feedback helps us to improve our content and services.

All feedback submitted is anonymous.

Thanks for the feedback!

Find out more about how feedback can improve digital experiences.