In a way, Working Effectively with Legacy Code does what it says on the tin: gives advice on how to work with legacy code. But while the title is technically correct, it is also misleading enough to be harmful.
After all, I have been working with legacy code without any help, and making progress cleaning it up, too. So why would I need a book? What can it possibly do to help, other than commiserate and advise me to refactor? For a long time I dismissed the book as “probably won’t help me much in practice”.
Well, it turns out that the book is not really about how to work with legacy code. Michael Feathers has his own, very specific, definition of legacy code: for him it means code without tests. And he also has a very specific definition of working with legacy code: for him it means adding no more untested code. Whenever a change or a new feature is needed, the affected parts of the code base need to be made testable first.
Redefining the problem from “working with legacy code” to “making untestable code testable” suddenly makes the topic appear very useful indeed, and the book turned out to be excellent. Like numerous other reviewers, I wish I had read it a year ago!
It’s full of practical advice, with lots of great examples, rather than high-level principles. Very pragmatic, where other software design veer off into idealism: “you often need to make your design worse before you can make it better” was a recurring theme here that I really sympathise with. It was a very inspiring rbook, making my fingers itch to refactor. On top of that it’s interesting, well-written, and very obviously written by an experienced, knowledgeable expert.
The book is structured sort of like a cookbook, question-answer style: “I need to make a change. What methods should I test?” I cannot imagine using the book to look up answers to such specific, disjointed questions. No, the book needs to be read and digested, and then re-read and re-digested, repeating until you’ve fully internalized all its ideas. I’ve read it once from cover to cover (skipping only the C/C++ specific advice) and I know I will be browsing it for inspiration many times. The numerous cross-references make it a very browsable book.
However. I like the general idea of making all legacy code testable, but in practice it’s not always practicable. GUI code is the most obvious example: it’s hard to test in the best of cases, and in a legacy code base, making it testable is a hopeless battle. I’m not going to refactor an entire 1500-line aspx page so that I can test my one-line change which makes a certain element invisible in certain circumstances. And more generally, making legacy code “non-legacy” is a worthwhile goal, but sometimes the change really is small and safe enough, and entangled enough, that making it testable is just not worth it. But I guess the book needs to be more categorical than reality: aim for the stars, get to the treetops.