I am trying, with only partial success, to apply what I’ve learned in Working Effectively With Legacy Code by Michael C. Feathers.
Feathers is a huge advocate of test-driven development. He puts it out there on page xvi: “Code without tests is bad code.” He defines “legacy code” as, strictly speaking, any code that isn’t already under unit tests. At first it struck me as a funny definition, because obviously lots of code is written today–even by me–without unit tests, and how can it be right to refer to software nobody’s even thought of yet as “legacy”? But for purposes of the book it works.
The bulk of this text consists of techniques for making changes in existing code, either while bringing into a unit testing regime or while carefully working around the lack of one. Thoughtfully, each of the chapters is titled with a question or complaint, to wit:
- How Do I Add a Feature?
- I Need to Make Change, but I Don’t Know What Tests to Write
- My Test Code Is in the Way
and so on.
Within each chapter, Feathers gives one or more coping techniques. The effect is quite a lot like that of Design Patterns by Gamma et al., sort of an indexed glossary of solutions. With names. Throughout the text, solutions are referenced by name and page number, such as when the Sprout Method (59) solution suggests use of Pass Null (111).
And that’s the one I want to talk about today: the Sprout Method solution. I’ve used this a lot recently and it makes sense even if it looks weird at first.
Here’s the problem.
You need to add some code to a method, you might need to do it in multiple places in the system, you want the changes to be unit-tested, and you need to do it right away without a lot of refactoring.
Here’s the solution.
You write the added code as a new method and call it where needed, rather than typing the new code in every place it goes.
This can be kind of ugly at first, because your original method might have thirty lines of logic-logic-logic and then there’s this one crazy method call in the middle of it that does about five lines of stuff and it’s not obvious why you didn’t put it in the main method.
But here’s the thing. The new method can be put under unit test, and then you’ve gained a little bit more control over the next change and the change after that. Maybe bits and pieces of the big methods migrate into your little new method over time. Maybe your big methods eventually turn into lots of these little methods, and then you realize the big methods are now completely unit tested anyway and you’ve succeeded!
What it’s good for
If today were endless, we’d spend it refactoring everything into logical units and placing it all under test with JUnit or NUnit or the like. That would be cool. But alas, the world is not that cool, and we don’t get to muck around with code that already works just to make it hypothetically easier to change in the future. What we can do is get the most benefit from the changes that we have to make anyway.
So Sprout Method (59) is a good start. Long story short, when you need to add a few (or more) lines of code to an existing method, consider making the new code a testable method of its own.