Legacy code is the “original” code present in websites or applications, but which has become obsolete or difficult to maintain. It is called legacy code because it is usually code inherited from another developer.
Developers are often afraid to “break” this code by modifying it, because the lack of automated tests at the time of its creation or its lack of documentation makes it fragile. Like a Jenga, modifying a piece of this code can lead to other malfunctions (sometimes quite random).
So how can we improve this code? Should it be fixed in spite of the business or the service that needs to continue? What are the recruitment issues related to too much legacy code? These are the questions we will try to answer thanks to Etienne Pinault, Mobile Team Lead at Sightcall, Jérémy Laplanche, CTO at Imparfaite and Gabriel Pillet, CTO at Web^ID.
“Having legacy code is not a failure, it proves that the application has been successful over time, despite the technical debt. In any case, any code, no matter how beautiful it is written today, will be the legacy of tomorrow. It is still important to come back regularly to refresh the code and progressively remove the technical debt” explains Gabriel Pillet, CTO at Web^ID.
Indeed, there are 3 types of legacy code:
Disclaimer: In this article we will rather discuss the second type of legacy code. However, it is necessary to keep in mind that there is legacy code that is very clean.
It is rare, when we talk about legacy, that we refer to a perfectly clean code base. Indeed, in our daily exchanges with developers or CTOs for example, a negative connotation is almost always associated with the idea of legacy code.
The concept of legacy code can be understood quite easily: imagine it as the construction of a Lego.
You are faced with a Lego that was built years ago with parts that no longer exist today.
And then you are told: “you see this Lego construction, you have to correct all the parts that are no longer on their axis and above all we want it to be completely articulated in two days' time”.
Add to that the fact that the person or persons who built this Lego at the time are no longer around, that you don't have the construction manual and that it is impossible to get hold of it.
Now that we have the definition, let's take a look at what legacy code looks like:
and above all: there are no unit tests!
If the code base of your application looks like this, then we can say that your project has a large technical debt. The problem is that the more technical debt you have, the more expensive it is to maintain.
The concept of technical debt appeared in the 1990s and allows us to analyse the durability and evolution of IT developments over time.
There can be many causes, but four explanations are most often at the origin of technical debt:
“As a CTO, having a “tech” vision of a project is essential, but even the most perfect project, if it is not in line with business expectations, will not work. You have to keep in mind that in order to evolve the service, it has to be “profitable”.
That's why in the first six months we did a lot of things that weren't very clean, but for the last nine to ten months everything we do is intended to be done more correctly. It's far from perfect, but as we go along, we're clearing up the technical debt and putting the right tools in place,” analyses Jérémy Laplanche, CTO at Imparfaite.
“At Imparfaite, what happened was that the team was made up of people who came in from time to time and left. So they were given an assignment, they did it, but they didn't necessarily look closely at the output. It worked, so much the better. Except that as time went on, we found ourselves with a pile of bad code on the project to the point that when I arrived, we were on the verge of the whole code breaking” explains Jérémy Laplanche.
In 2017, one of the world's largest credit rating agencies, Equifax, suffered an attack by hackers who had used a security hole in the company's computer system to their advantage. The data of 148 million people worldwide was compromised.
However, a report by the Government Accountability Office in the United States concluded that the breach was entirely preventable and was partly caused by legacy code on the website that dated back to the 1970s...
In addition to the clean-up costs, which have been estimated at $1.3 billion, security breaches have a major influence on the confidence of your consumers and the public.
Too much technical debt can cost a lot in maintenance and refactoring. Refactoring is the act of going back over the code base to clarify, debug and clean it up: this has a cost since an internal or outsourced human resource must commit time to such a technical subject.
As mentioned above, in order to respect the delivery time constraints of a project, the code is developed in a functional but technically imperfect manner: this almost necessarily implies a code rework or refactoring phase in the long term.
According to a study by the Consortium for IT Software Quality, legacy systems cost American companies $70 billion in 2003, compared with $596 billion in 2018.
Refactoring (i.e. cleaning up code without changing its behaviour) takes hours and is often placed at the bottom of the task list. In fact, only between 11% and 30% of development time is allocated to technical debt in companies. Of this time spent, studies have shown that about 50% of the time spent maintaining code is actually spent trying to understand the code they are trying to maintain!
We can attest to the fact that the presence of significant technical debt is a real barrier to the recruitment of new tech talent. Depending on the state of the legacy code, recruitment is not impossible, but it is more complex. The mistake to avoid is not to mention the reality of the legacy code during recruitment interviews, because this is the best way to ensure that a developer is disappointed and considers leaving the job in the short term.
To alleviate the difficulty of recruiting with a large technical debt, it is better to be transparent about the real state of your code, or even to show it during the recruitment interviews. In this way, you ensure that you only recruit profiles who join your project with full knowledge of the facts and who have the desire (or even experience on the subject) to take part in a code improvement and refactoring phase.
“A code that is too legacy with a high technical debt can put off new candidates for hiring, who are often looking for projects with modern, state-of-the-art technologies. It is therefore more difficult to recruit when the technologies used are too late and no longer current.” explains Gabriel Pillet, CTO of Web^ID.
Even if tackling a large legacy project can put off junior profiles, this type of project can, on the other hand, offer a great challenge to experienced developers.
“Some senior profiles are happy to be confronted with the problems of refactoring legacy projects, limiting technical debt, improving quality and stabilizing the code often offers more challenge than starting a project from scratch,” says Gabriel Pillet.
Simply because it means taking the risk of cutting a service that works.
If the original developer understands his code de facto, this may not be the case for other and future team members if the code does not respect development best practices.
Moreover, over the years, the team evolves and the code becomes a real patchwork that is increasingly difficult to understand and modify. Until it becomes impossible to make updates or changes because no one understands the code and its effects anymore.
"A legacy code, even if it is technically indebted, you don't touch it. If it works, we let it work and there is no reason to change anything. That's why taking an application from scratch doesn't make sense, because the service will work the same, it will just be better written; but that in itself is irrelevant.
If there's a feature or a piece of code that doesn't work or that you need to change for product reasons, then you can change it. But in general, as long as it works, we don't touch it, because we risk breaking everything for not very much," explains Etienne Pinault, Mobile Team Leader at Sightcall.
Jérémy Laplanche, CTO at Imparfaite, explains that "you have to know how to juggle two aspects of development: the technical aspect and the business. For a CTO, it is necessary to know where you can afford to add technical debt with commentary, tests, etc., while ensuring that you don't break anything as you go along. The most important thing is to keep in mind that we will never achieve zero technical debt, what we need to do is limit it as much as possible.
In order to avoid technical debt, it is necessary to start by putting in place good development practices.
“In the audits we carry out, we find that the use of a framework greatly limits drift, whereas "home-made" code is almost always linked to a large technical debt and leads to having to redo a new version of the application,” says Gabriel Pillet, CTO of Web^ID.
Why is it better to create tests before the code?
Quite simply, because if you tell yourself that you'll end up testing once the code is written, there's a good chance you'll never do it. Typically, the testing stage is relegated to the end of your to-do list and, in the end, this stage is often reduced or even eliminated due to lack of time.
“The good idea is to refactor in small chunks, and have it introduced by a new feature. That's the best thing to do in my opinion, because you're going to satisfy both the business by introducing a new feature that will be useful to them, and the development team, because you're going to reduce some of the technical debt. You can't reduce technical debt just to reduce it, there has to be something else to go with it,” explains Etienne Pinault, Mobile Team Lead at Sightcall.
In short, you can't avoid legacy code, so the best thing to do is to be patient and technical!