In part one of this series I discussed what I believe is the most important reason to do TDD and that is for programmers to have a way to preserve their intentions. In the second part, I explain the benefit that TDD gives a development team in the area of documentation. In this part, I would like to discuss the effect that TDD tends to have on software design.
When we discipline ourselves to consider the end result of our programming efforts, it focuses our minds on the reason that the software is being made. Our thinking shifts from the effect, to the cause. By forcing ourselves to document the test first, we must do enough investigation and thinking to determine what is being requested. One way that this kind of thinking improves code design, is that it ensures that the developer is the first user of it. It requires that we experience what using our software is really like. Since we don’t like complicated interfaces, and since we can only imagine a limited amount of complication in our minds, we tend to naturally test against a simple interface and this makes the system easier to use for others too.
When we force ourselves to consider our tests first, it makes us expend more mental energy than we would if we were to just make the code. This also challenges us to consider if what we are doing is really worth doing at all. It has long been documented that 80% of our software is rarely if ever used. Could it be that some of this is because software is easy to write but not easy to use? I haven’t done a scientific study to determine whether or not TDD would change the 80/20 rule, but my experiences as a test-driven developer have demonstrated to me that I have avoided unnecessary code when doing TDD because of the extra effort required to test it.
Another thing that TDD does is that it amplifies bad code designs. That probably doesn’t sound good at first, but it really is. It has been obvious as I have practiced TDD that when code is hard to test, it’s often because the design of the code itself is overly complicated. This is especially obvious when attempting to “back fill” tests by writing them last instead of first. One of the common mistakes that new test-driven developers make is to assume that if a test takes a lot of mocking, it must mean that TDD is not very helpful. Actually, when TDD is hard, it’s often pointing to a design that could use some work. There are other reasons that TDD seems hard to new practitioners and I hope to share that in another post. The point here is that when TDD creates ugly tests, it’s actually a benefit to us because it shows us the bad design earlier rather than later. Good designs have clean and understandable interfaces. Tests use interfaces and when those interfaces are not very intuitive, it tends to become obvious.
Another design benefit of TDD is the effect it tends to have on names. How we name our classes, methods, functions and variables changes the readability of the code. If code is easier to read, it’s much easier to maintain in the future. That’s an important measure of software quality because software usually spends most of its life in maintenance mode. The way that TDD improves names, is that it causes developers to think in terms of the problem domain. When we test first, we focus on exposing a problem, so we tend to use wording that is a part of that problem’s domain. When we write the code first, we often focus on our solutions. We may use words like “loop” or “string”. Those types of words don’t express the problem we are solving but the way that we are solving it. I have found that when I use TDD, words from the problem domain find their way into public methods. They may go deeper than that as well. I don’t believe that TDD automatically generates a system with good names, but it appears to help us start off with better names than we would otherwise.