Redefining Legacy Code
There is something we all can agree on: most of the developers don’t like to work with legacy code. For this reason, most of the things we read online about legacy code are about bashing, rewrites, and how it’s just bad code.
Most of these conversations are based on outdated definitions, maybe because with such definitions, and the status quo being that legacy code is poorly written code, everyone can feel much better with the code they write every day. The problem is that code will soon also be legacy if we don’t do something about it and not by the definition you are thinking.
Code without tests is a bad code. It doesn’t matter how well written it is; how well structured it is; how well-encapsulated it is. Without tests, there is no way to tell if our code is getting better or worse.
Michael Feathers in his book “Working Effectively With Legacy Code”
Commonly the discussion around legacy code get’s mixed with the code being good or bad. It’s also common to refer Clean Code as the nemesis for legacy code. And that’s a big part of the problem on how we look to legacy code. And that’s also why the commonly accepted definition of legacy code is “code without unit tests”. This definition comes from Working Effectively With Legacy Code by Michael Feathers.
According to this definition, as long as your code has unit tests it will never be considered legacy. Don’t you find this weird? If you do, you probably noticed that eventually, no matter how many tests you had written, your code will become legacy.
Nevertheless, this is a generally accepted definition of legacy code. And it has been like this for the last 20 years.
The term “unit test” comes from electrical engineering. People say that in software development, the term was introduced by Kent Beck in 1997 when he released JUnit.
This little historical reference is to remind that at the beginning of the 2000s it made sense to say that legacy code didn’t have unit tests. Anything developed in the previous ten years would for sure not have unit tests. And when those systems needed to be replaced or refactored, developers who had already started adopting unit tests, noticed that without them it was a hard work to do.
But does it still make sense in 2022? I don’t think so. The kind of legacy we deal with today is very different. Most of the code written over the past 10 years has unit tests. And many of that code is still hard to maintain because, guess what, you can still write bad code because you can always write bad tests on top of it.
Our problems are different today. Nowadays the lifespan of software is much shorter. According to the Flexera Manufacturer Product EOL Report, the average lifespan of a software product from the biggest manufacturers is around 5 years. But if we look more in detail, each version of the most used programming languages has a lifespan of around 2 years for stable versions. If we get into the details of frameworks, the age of supported releases descends in some cases to 6 months.
This means, that if you don’t upgrade your technologies in as short as a six-month span, you will be using outdated, non-supported, technologies. And that’s what you should call legacy code: code that is based on outdated technologies and is no longer maintained.
That’s why systems can quickly become legacy today. Because of the world around us moving much faster not only on the tools (IDE’s, languages, and frameworks) but also on platforms (operating systems and devices).
So it’s time to deprecate the outdated definitions, after all, we are building the future.