8 Tips For Working With Legacy Code
Dealing with legacy code can be a chore. Some developers even call it “legacy code hell”.
But, unless you’re starting a project from scratch, it's inevitable. And that means you need a better way to work with it.
What Is Legacy Code?
Legacy code has a different definition, based on who you ask. Some say it refers to any code after it's been written. Others assume it is old code. Or that it’s spaghetti code.
The Classic Definition of Legacy Code
Legacy code refers to any code maintained by someone who didn’t write it.
New Definitions of Legacy Code
Today, it refers to code that you don’t understand and that’s difficult to change.
This can refer to code inherited from someone else. Or to code inherited from an earlier version of the software. Or it can refer to code without unit tests.
Working With Legacy Code Is a Challenge
There are challenges in working with legacy code. And the biggest challenge might be your assumptions about it.
You might think the code is bad. Whoever wrote it didn’t know what they were doing. You could have done a better job.
But the truth is, there usually is a reason why the code is how it is. And if you didn’t write it, you might not know that reason.
That’s why care needs to be taken when making improvements to the legacy codebase. You can’t just put a quick fix on one area. There might be some dependencies you’re unaware of.
And that’s why it’s important to know when to maintain the legacy codebase and when to change it.
How to Work With Legacy Code
You can’t improve the legacy codebase overnight. But you can take gradual steps to improve it.
Whether you’re just getting started with a legacy codebase — or you’ve been working on one for a while — here are eight tips for improving it.
1. Test the Code
One way to understand the code is to create characterization tests and unit tests. You can also run a over your code to identify potential problems.
This will help you understand what the code actually does. And it will reveal any potentially problematic areas. Once you understand the code, you can make changes with greater confidence.
2. Review Documentation
Reviewing documentation of the original requirements will help you understand where the code came from.
Having that documentation handy will help you improve the code — without compromising the system. Without this information, you could accidentally make changes that introduce undesirable behavior.
3. Only Rewrite Code When It’s Necessary
Rewriting legacy code is tempting. But it’s usually a mistake.
It takes too much time and too many programmers to rewrite everything. And even if you do it, rewriting code can introduce new bugs. Or it can remove hidden functionality.
4. Refactor It Instead
It’s better to refactor legacy code than rewrite it. And it’s best to do it gradually.
Refactoring is the process of changing the structure of the code — without changing its functionality.
This cleans the code and makes it easier to understand. It also eliminates potential errors.
To refactor (without risking mistakes), it’s best to:
- Refactor code that has unit tests — so you know what you have.
- Start with the deepest point of your code — it will be easiest to refactor.
- Test after refactoring — to make sure you didn’t break anything.
- Have a safety net — e.g., — so you can revert to a previous build.
5. Make Changes in Different Review Cycles
Don’t make too many changes at once. It’s a bad idea to refactor in the same review cycle as functional changes.
Plus, this makes it easier for code reviews. Isolated changes are much more obvious to the reviewer than a sea of changes.
6. Collaborate With Other Developers
You may not know the codebase very well. But some of your fellow developers probably do. It’s much faster to ask questions from those who know the codebase best.
So, if it’s possible, collaborate with someone who knows it better than you do. A second set of eyes on the code may help you understand it better.
7. Keep New Code Clean
There’s a way to avoid making the code more problematic. And that’s by ensuring new code is clean. It ought to be written to adhere to .
You can’t control the quality of legacy code. But you can make sure that the code you add is clean.
8. Do Further Research
Working with legacy code gets easier with time. A junior developer may not understand why a codebase hasn’t been refactored (and may be keen to refactor it). But a senior developer will know when to leave it alone.
Learning more about the codebase will help you improve it.
A good starting point is “” by Michael C. Feathers, which contains some good examples of how to make changes to the codebase.
Another good source is “” by Martin Fowler. This book offers many tips for effectively refactoring code.
Tools for Working With Legacy Code
You’ll always need to work with legacy code — or work around it. After all, the code is there for a reason. It works. And its results may be good enough that you can let known issues go.
There are good reasons for making changes to code, too. You might be adding a feature, fixing a bug, or improving design.
In a perfect world, you’d continually rewrite that legacy code until it’s fully debugged. But chances are, that won’t be practical.
So, what you need to do is figure out what you can change — and leave the rest alone.
Static Code Analysis and Legacy Code
One way to do this is by using a . You can set a baseline and then run analysis on the new code to make sure it’s clean. And you can suppress results from your legacy code.
, for example, makes this very easy to do.
Analyze Legacy Code
Helix QAC can check your legacy code against rules, typically from a . You’ll get diagnostics of violations. And you can prioritize them by severity. This means you can focus your attention on fixing the most error-riddled pieces of legacy code first.
You can also set your legacy code as a baseline. Maybe the code is fine as-is, and you want to leave it alone. Setting a baseline means that legacy code won’t be pulled into your diagnostics. Instead, you can focus on finding issues in new code — and ensuring that’s clean.
You can also use suppressions to create exceptions for your legacy code. So, you can essentially dismiss violations in legacy code. You might set your suppressions on specific rules or violations within a particular category.
In some cases, you may be reusing legacy code from one project to another. But some legacy codebases weren’t developed with coding standards. And if you need to achieve compliance (e.g., with ), this can create problems. By using , it’s easy to see where the errors in your code are.