I recently talked to another developer and mentioned that I use TDD. The other person then asked me whether I'm also one of that religious TDD followers? I had never thought about this. Do people think I'm preaching to them when I encourage them to use TDD? Are they only doing it to not hurt my feelings because they think I'm a believer? Did I create an atmosphere where TDD is a dogma that cannot be questioned?
Let's talk about TDD, why it's not a religion, and why I think it's so much more than a practice to write code.
If you're treating TDD like a religion you should probably reconsider your life choices. Because following something based on dogma won't do you any good. This article focuses on:
- why TDD is not a religion,
- why I, personally, like to work with TDD, and
- that TDD is really not only about testing
Why TDD is no religion
Just because. Because you shouldn't blindly follow any practice to write code. If you think you need to preach anything work-related please stop. Life is too short to get into fights about whether yours or someone else approach is better. You're probably both wrong. At least to some degree. There simply is no perfect, one-fits-all solution to anything out there.
Keep your mind open. Assume that no one gets up in the morning to do a bad job. Be mindful and your stress level will decrease a lot.
Why I write tests first
If you don't like my reasons then you might want to read what Kent Beck has to say on the matter. This section is purely my personal opinion. If you have strong feelings about any of the claims I make I encourage you to reach out to me (find out how in the last paragraph). I'm more than interested to hear your thoughts.
Create APIs from the perspective of the consumer
When you start with nothing you can free yourself from thinking about the details. I know that the test will be failing in the beginning. Even more so, I want the test to fail initially. When I create an API that does not exist yet I can dream up anything I want. By doing this I can make sure that the DX is great. Because when you start from the other side you often end up with APIs that leak details about your implementation. However, when there is no implementation yet you cannot leak anything. This can help you to build minimal APIs that are easier to consume.
Keep me from doing too much
When I start on a new and exciting topic I can get ahead of myself. If I directly start coding and already have a couple of use cases in my head I can end up in a situation where there is a lot of code and the borders between the use cases start to get blurry. Sometimes they even start to influence each other. Tests help me to stick to one use case because I can follow some simple rules. When I write a test first I know that when the test goes from red to green I'm done. If I think that I'm not done yet this means that I need to add another test case. Tests help me split a large task into small units of work.
Make sure the test points to the correct problem
This is one, if not the most important reason. When I write the test first I'm forced to see it fail. I then need to verify that it fails for the correct reason. While doing this I not only make sure that the test targets the correct problem but also that the error message is descriptive enough so that others in the future have enough context should their changes break the test.
You might lose all that when you write your tests after you added the implementation. I've seen it too often that developers tend to write tests that replay what they have implemented. That does not prove anything. Even worse, these kinds of tests fail for any number of reasons that have nothing to do with what they should verify.
Help future me pick up the work tomorrow
Context switches suck.
Even when I continue with the exact same task the next morning I need some time to figure out where I left off and what I need to do next. What helps me a lot is to end the day with a failing test. This way past me sets a pointer for future me for where to pick up the work. I can get right into coding again and make that test green. After that first test, I'm usually back on track.
Give me serotonin rushes every couple of minutes
Each time I finish something I'm happy. Ending a day with the feeling that I haven't accomplished anything is super frustrating. When I code the whole day but can't be sure that what I've done actually works then this makes me sad. But when I TDD myself through the day I have that feeling of accomplishment every couple of minutes. Even better, I can be sure that everything I've done so far works as expected.
This feeling of getting sh*t done and the serotonin rush that follows is probably also the cause why I got addicted to TDD. Right now it's more like a drug (in the most positive way) than anything else.
TDD is much more than writing tests
Here's the kicker. For me, the biggest advantage of TDD is not the tests. I see TDD as a great forcing function to help developers solve the right problems.
When a developer sits down starts with a test she needs to understand the problem first. I would claim that it's close to impossible to write an assertion when you don't know the problem you're trying to solve. At least, it will become much harder to that. If our developer realizes that she lacks some information this happens before she starts building the new functionality.
I'd like to point out that the most important aspect here is that all this happens at the start of the task. This means:
- She hasn't spend days writing code she might need to delete later
- She is not frustrated because she has not invested much time yet
- The person who helps her will not need to understand any implementation details
Not having a possibly wrong implementation frees you from a whole set of issues as well. Without code there you will not have to rework anything. Also, you can't get attached to anything you've already built and try to justify keeping it in regardless.
If you figure out that the problem is not entirely clear then you can clarify it. That's also great because there cannot be blame at this point. Why? Because when the resources you work with aren't enough for you to figure out the problem then it cannot be your fault. At some point, someone has probably made some assumptions and you merely discovered these shortcuts. By asking for clarification you are the hero in this story because you are now making sure that you're working on the right thing.
My personal experience working like this is that it improves the atmosphere a lot. It's easy to get mad at someone for spending days of work but not solving the actual problem. I have caught myself asking Why didn't you ask me earlier?. But then I also realized that the person didn't realize that he was going in the wrong direction. With TDD in place, people stop to ask for help and start to ask for clarification. That's much nicer. Because then the person who asks doesn't try to undo a mistake but actively seeks out clarification do avoid doing that. In my experience, this leads to solely positive reactions.
To end this post on an even more meta-level I'd like to propose a name change. I believe that by working with TDD we will end up with something that I would call CDD or communication driven development. Because this is what I believe is what helps development teams the most. To talk to each other and to do this as early and as often as possible. We talk a lot about feedback loops. But I think people, especially developers tend to think of feedback as something that comes from customers or managers and that means more work. Making sure you're working on the right problem is another form of feedback that is cheaper to get and will already help us a great deal.
What is your experience with TDD? Do you use it for your company? If yes, would you agree with my observations or not? If no, does this article encourage you to try it out? Tweet @philgiese with your ideas and questions.