On Friday, March 10, I attended the Working Effectively with Legacy Code workshop hosted by Michael Feathers, along with a few other members of my team. We maintain a difficult legacy code base and we were eager to do some practice and learn some useful techniques.
The most important lesson I learned is that any effort to improve an entire code base is almost certainly going to fail. If you have a legacy code base that is hard to maintain and with a limited number of tests, it’s very unlikely that you will make it beautiful, with a very high code coverage. You will never have the time and it is not a wise move.
Stop worrying about making the entire code base clean.
Stop worrying about increasing your code coverage to 100%.
So, what should you do instead?
You need to find a clear reason for change! This is what agile is about and most of the time the reason for change is delivering some value to your customers in the form of bug fixing or new features.
Focus your improvement efforts on the area of the code base that can help you to achieve the project goals.
The Michael Feathers approach
Michael Feathers suggests a very disciplined and safe way for dealing with legacy code:
- Identify what you want to deliver to your customers and why
- Identify the areas of the code that will be affected
- Break dependencies to make the code testable
- Write characterization tests to fully test those areas (and possibly make them clean)
- Make the changes with tests (using TDD where possible)
The main dependency breaking technique is to use a Parameterized Constructor. It basically consists of using constructor injection to provide your tests with the ability to mock a dependency. There are other techniques but this one is the most common and cleaner.
In order to apply the Parameterized Constructor, you need to be confident using the Extract Interface, Extract class and Move methods for refactoring. For .NET developers with experience of R#, this is very easy to do. I don’t think I have used those refactorings often enough, but now I feel a lot more confident in my ability to use them (and I’ve already done it in product code since the workshop).
The focus is on breaking dependencies in order to put a system under test. So don’t be scared to make your software a little bit worse in order to increase testability, and don’t limit yourself to changing production code only for production reasons!
Another very interesting technique I learned is called Revealing Interfaces and it is nicely explained on the Michael Feathers blog. I have already used it on my project at work with success, and it can be very useful to understand how two different modules of your software interact with each other.
Scratch Refactoring is yet another useful technique. I already knew about it but I haven’t used it often enough. It simply consists of taking the code base and brutally changing it for the sake of understanding it. You don’t care if you break it because you know you will throw away the code. Sometimes it can be the only way to understand a very complicated piece of code.
This post was originally published on Andrea Angella’s Software is My Passion blog, where you can read more of his thoughts on productivity, teamwork, craftsmanship, C#, and .NET.
Also in Blog
In this three-part series, guest bloggers from DevOpsGuys look at the real role of Ops in DevOps. Where it changes, how it changes, and why Ops has an important part to play in the brave new world of ...
Also in Software development
A data discovery and classification research project from Foundry
In Foundry, we’re responsible for developing new products and technology to support the changing needs of our customers. We’ve ...
Also in Working at Redgate
When someone buys software from Redgate, it’s often not the end of the story. It can be the beginning of a journey because support is frequently part of the package.
There’s good reason for that ...