On Friday, March 10 I attended the Working Effectively with Legacy Code workshop with Michael Feathers with few other members of my team. We are maintaining a difficult legacy code base and we were eager to do some practice and learn some useful techniques.
The most important lesson I have learnt 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 and 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 suggest 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 characterisation 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 using Parameterised Constructor. It basically consist of using constructor injection to provide your tests the ability to mock a dependency. There are other techniques but this one is the most common and cleaner.
In order to apply the Parameterised Constructur you need to get confidence in using the Extract Interface, Extract class and Move methods refactorings. For .NET developers with a license of R# this is very easy to do. I don’t think I have used those refactorings often enough, now I feel a lot more confident in my ability to using them (and I already did it in product code since the workshop). The focus is 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 at changing production code only for production reasons!
An another very interesting technique I have learnt 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. It can be very useful to understand how two different modules of your software interact with each others.
Scratch Refactoring is yet an another useful technique. I already knew about it but I haven’t used it often enough. It simply consist in taking the code base and to brutally change it for the sake of understanding. 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.