The benefits of test-driven development
The benefits of test-driven development are numerous. However, this time, I want to focus on some specifics related to software design.
When we write software, we often solve many problems simultaneously. Some of these problems are:
- Decomposing the problem into smaller sub-problems
- Designing how the new behavior will be invoked
- Figuring out how to make the system exhibit the desired behavior
- Doing all of the above in a clean and maintainable way
Surely there are other things to consider, but for me, these are the most important, no matter what I’m developing.
How does test-driven development help? By forcing me to focus on one of these problems at a time.
Test-driven development starts with the test list. (By the way, 99.9% of all the TDD material out there completely forgets about this part.) By creating a test list, I am practicing behavioral composition.
Each test on the list is an example of a new, small behavior I want to introduce to the system. The final solution is a composition of many small behaviors.
Once we have a list of small behaviors that we need to implement, we can think about which of these behaviors would make the most sense to implement next. I’ve written a bit more about this part in the test lists article, so I won’t go too deep into it right here. It’s enough said that this is a problem on its own and test-driven development makes that problem explicit.
After I’ve picked a test and started writing it, I’m faced with many decisions. I have to decide how my new behavior will be invoked. Should I create a new class, or just add a method to an existing class? What kind of parameters will it take? What should the types look like? These are all important questions that I need to answer. Again, test-driven development throws this problem in your face and forces you to address it. If I can’t answer these questions, then what am I even doing?
The test serves as an example of a behavior. It tells you how the behavior will be invoked, what kind of data you need, and what should happen as a result.
After the test has been written and I’ve seen it fail, it’s time to implement the desired behavior. Here, test-driven development helps keep us focused on a single problem: making the test pass.
So, let’s implement the simplest, quickest possible solution to do exactly that. It doesn’t matter if it’s the ugliest piece of code I’ve ever written. I just want the test to be green.
Finally, I have a working solution. The test is passing. The solution is the ugliest thing in the world. But it works.
This is where I can think about how to refine it. Let’s apply all the years of software engineering knowledge and experience to make the solution simple and maintainable.
Each step in this process is one little step. For simple-minded people like me, this makes software development manageable. It lets me focus on implementing one little behavior at a time, and doing so in tiny little steps. That is, in my opinion, the real benefit of test-driven development.