Today I finished reading Working Effectively with Legacy Code by Michael Feathers. If you are not familiar with this book, you might be wondering if I’ve somehow been roped into maintaining some sort of 10 year old VB6 application -fortunately this is not the case. But I am indeed working on a legacy application – at least, it is legacy by Michael’s rather unique definition. Michael defines legacy code as code that is not under test, specifically unit tests. By Michael’s definition, virtually all of the code I have written during my career was legacy from the day it was born, and when I reflect on it, this seems like a fair statement. Here is a short excerpt from the preface:
Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behaviour of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.
It doesn’t matter how clean and refactored my code was – developers that had to maintain it were forced to do so with uncertainty. It is specifically that element of uncertainty that I associate with the legacy code experience.
The book uses a FAQ format, with chapter titles such as:
- I need to make a change. What methods should I test?
- I can’t get this class into a test harness.
- This class is too big and I don’t want it to get any bigger.
- I need to make a change but I don’t know what tests to write.
It does a great job of illustrating refactorings that let you break dependencies so you can get the code under test. These refactorings are designed to be performed safely without tests so you can get the tests written and then move on to more significant refactorings. The book also does a great job of tackling the mechanics of writing the tests themselves.
I think the book could have been better named. Ayende says that it should be mandatory reading for people that write code for a living. I agree with him, but the title does not communicate this. In general I found it very readable and relatively quick to get through, and the lessons the book offers are profound.
So the obvious question is whether it has changed the way I’ve been writing code. Well unfortunately, I wrote a bunch of legacy code just last week! By night I revel in the freedom that my unit tests give me as I work on Fluent NHibernate, but by day I continue to work in a manner that Uncle Bob would consider unprofessional. Why?
Unfortunately the book doesn’t have a chapter called “I’m all alone, nobody else is convinced that writing unit tests is worthwhile. Is it worth starting if its just me doing it?”. Nor does it have a “This is just a quick fix for someone else’s broken code, I’ll write tests when the –real- work begins” chapter (its a weak argument but I’ve found it an easy trap to fall into). The closest the book comes is the very last FAQ chapter: “We feel overwhelmed. It isn’t going to get any better”. But at less than 2 pages, it didn’t quite deliver what I needed to hear.
I’m finding it difficult to take that first step and add that 55th project to the solution (yeah…) so I can write some tests. Part of it is procrastination, but part of it is fear. I believe these ideas have value and I don’t want to screw it up. But simply “waiting until the time is right” is the refrain of the procrastinator.
This makes me feel that Working with Legacy Code is a great book, but not an entirely complete package. It gives you the tools and techniques but assumes that the reader can simply read it and run with it. Michael developed these techniques as he worked with teams that heeded his advice and were prepared to follow his lead. A piece of the puzzle is missing because Michael has been the consultant for so long.
Maybe this criticism is unfair. People can write books full of advice, but its up to the reader to start putting that advice into practice. A new work week begins tomorrow, perhaps I’ll add that 55th project before I do anything else tomorrow morning.