A common complaint against Test Driven Development is that writing tests and refactoring take too long. In the long run, I’ve found that TDD has improved my skills such that I can complete work faster by writing tests and refactoring than without. I’ve also found that this information is a weak argument for those who have less confidence in their skills, or feel too pressed for time to learn. But that’s not the only benefit.
There’s an old carpenter’s saying, “Measure twice and cut once.” It’s better to know what you want before you spend time and materials. It’s hard to cut off a thin piece of board if you cut long, and impossible to restore it if you cut short.
Writing the test first, to me, feels much like this dictum. It’s easier for me to write code when I specify exactly what result it should give for a given situation. Before I wrote test-first, I often had to go back and re-examine some code for a boundary condition I hadn’t considered. Coming back to this code was more work than considering all of the boundary conditions when I wrote it. With tests, it’s easier for me to notice which boundary conditions I haven’t explored. With tests, even if I do come back for a missing condition, I find it easier, as the existing tests give me a base for checking the new condition, as well as helping me understand the code again.
So we should always think ahead so we don’t have to revisit the code?
Once we determine the desired dimensions of the timber, why don’t we just cut it exactly to that size? Framing carpenters will, of course, do this. The nature of their work requires less precision than that of finish carpenters or furniture makers. Even timber framing carpenters require significant precision at the joints where the structure goes together.
When the fit or finish is important, woodworkers might rough-cut the lumber to approximate size. Then they’ll mill it or plane it to the correct dimensions. If they’re building furniture, they’ll follow with sanding it smooth, first with coarser sandpaper and then progressively finer.
I find this analogous to refactoring code. At first, I make a rough cut, getting the code to work. Then I refine it: removing duplication, giving better names, restructuring to better reflect the nature of the domain, and such. With each refactoring step, I try to move closer to a more precise fit, and a smoother finish.
Why would I spend such effort when it’s not generally visible from the outside? Unlike the framing carpenter who built your house, I’m building structures that will be handled and modified over and over. Computer programs are rarely built once and then left alone, even when we think they will be. As Martin Fowler says, we need to keep software soft. Pieces that fit smoothly and with precision are easier to rearrange than rough cuts that bind or fit loosely.