Are You a Legacy Code Developer? This is a question that I’ve asked myself many times over the years, and it’s one of the reasons that I wrote Working Effectively with Legacy Code. Everyone has a different definition of what “Legacy Code” means.
For me, legacy code is simply code without tests. When you need to make a change, and you don’t have tests in place to give you the safety net you need to change the code with confidence, that’s legacy code.
Having worked with a lot of teams over the years, I have found that there are four common signs of refactoring in projects. These four signs are known as The 4 SIGS of Refactoring:
Smells in General (SIG1)
Strange Names (SIG2)
Inappropriate Intimacy (SIG4)
Are You a Legacy Code Developer?
Are you the kind of developer who doesn’t touch the code with a 10-foot pole unless you are fixing a bug? Do you know what methods are called on an object, but not sure where they came from? Do you avoid making a change in case it breaks something else? If so, then your application is suffering from legacy code.
A definition of legacy code:
“Legacy code is simply code without tests.” – Michael Feathers
You can have legacy code in any language and framework. It’s not always old code, but it’s most likely written by someone who didn’t know what they were doing. There are always excuses for not writing tests: “I’m on a tight deadline”, “I don’t have time to learn how to write tests”, “I’ll be out of here before anyone sees this mess”. These are short-term gains with long-term consequences. You’ll pay for it later when you have to fix bugs and make changes to the software.
People often ask, “How do you deal with legacy code?” I usually give them the same answer: “You get paid for writing code; you get paid for designing code. But you also get paid for dealing with legacy code.”
But who is a legacy code developer? Let me tell you a story.
A few years ago, I was working on a project that had been in development for several years. The first part of this project was in production, and the second part was just starting. I joined the team during the second part–right after they put the first part into production. The team had just hired a new architect. He was excited about joining the team and thought he could improve things. One day, he said to me, “You know what we need? A bunch of unit tests.”
I said, “Sure…but we need to refactor first.”
He looked puzzled and asked me what I meant. I told him, “We can’t write tests until we clean up some of this mess.”
He asked me an interesting question: “What do you mean by mess?”
I said, “Well, look at this class” (and I pointed to a class where one method did too many things). He replied:
Refactoring is an essential activity when you are writing software. Without refactoring there’s a good chance the code will deteriorate over time and become increasingly difficult to maintain. The problem with legacy code is that it often has not been refactored for a long time. This makes it harder to work with and more expensive to change.
Refactoring Legacy Code
How do you know if you’re working on legacy code?
The answer is easy: Just ask yourself whether the code base was around before you got there. If it was, then you’re working with legacy code. The only way around it would be to have been a part of the original team that built the system. And even then, it might not be as nice as you think.
I’ve often heard people say that they don’t have to worry about legacy code because they use test-driven development (TDD) or pair programming. It’s true that those practices can help reduce technical debt and make refactoring easier. But I still see TDD teams working in legacy code bases all the time.
When I’m talking about legacy code, I’m talking about any code without tests. That doesn’t mean that the code is necessarily bad; it just means we don’t know if it’s good or bad without tests. As Michael Feathers puts it in his book Working Effectively with Legacy Code:
“I like to say that software rots if nobody tends it.”
In this article, I’ll share some of my stories and experiences with legacy code, then provide four techniques for working effectively with legacy systems.
In the beginning there was legacy code. The software was written, it worked and it was shipped.
The code was modified, bugs were fixed and new features were added. The software became more robust over time and the company became more dependant on it.
Then the developers left the company. They were replaced, but the newcomers didn’t understand all of the nuances of how and why the system worked. They even started writing tests to prevent future problems, which they had heard about from their colleagues that had experienced this kind of situation before or read about it in books.
The legacy code base grew as more business logic was added every day and as time went by, only a few people really understood it completely: knowledge of how the system works became concentrated in fewer people’s heads.
And so it went until you arrived; this is where you come in. You are the one who will change things for the better; you are going to improve all of this while your predecessor is away on vacation – he gave you some nice tips to get started with before he left though.
You have just been assigned to work on some legacy code!
Imagine that you are working on a software product. It is used by lots of people, and the team responsible for it is large. You have been hired to work on this product because of your experience in writing code quickly and efficiently. You have been given a task to implement an additional feature, but as you start implementing it, you realize that it is not possible without changing some existing code. You change it, working hard to make sure that your changes will not break anything. When finished, you submit the feature along with the changed code for code review.
The reviewer says: “I am sorry, but the changes you have made aren’t necessary for the feature to work. We don’t need so many files changed – we can just add the function X() here and there.”
You are surprised because you know that this change was necessary – there was no other way around it!
As you write a reply, thoughts like “Why did I even bother making all these changes? What’s wrong with him? We need all of these changes!” run through your head.
You look at the screen and begin typing: “I’m really sorry… but this is not how things should be done…”