This post is coauthored by Andy Unterseher.
In his post titled “#NoTDD“, Eric Gunnerson of Microsoft laments the challenges surrounding TDD. He makes some very good points that demonstrate that TDD is not a silver bullet for most teams to achieve a high-quality design. The key failure outlined in the article is that dogmatic TDD as the only means of system design does not work in most dev shops due to a lack of highly skilled developer/designers. We agree. We must have a coherent software architecture/design to start with that is based upon encapsulating future change.
Starting with a good design is essential and the only way to effectively design software is to design for change, just like other engineering disciplines. With a good (not necessarily perfect) software design in place (based upon encapsulating change), TDD can be a powerful tool for developing highly cohesive, loosely coupled interfaces between classes and services and enabling future refactoring.
When we practice TDD, we are more focused on test-driven design than test-driven development. Developers are able to write tests either before or after their code as long as the focus when building the code is following the design and writing testable code.
Here are our top five benefits we have realized when doing TDD within a system with a coherent software architecture based upon design for change:
- Forces consumption awareness in your code because you create the first consumer
- Gets you focused on the interface rather than the implementation
- Things that are difficult to test tend to be simplified in order to achieve testability
- Tends to create interfaces that are conveniently callable
- Requires more decoupling from its surroundings
- Identifies code that should be isolated to enable easier testing
- Increasing the cohesion of the classes/services/modules as you gain additional insight into the volatilities in the system
- Tests allow you to play “what if” games with broad changes to assess the impact to the design
- Makes the code more understandable/readable
- Unit tests that describe how the developer intended the code to be consumed
- Built in example code!
At Don’t Panic Labs, we are devoted disciples to this design discipline and the use of TDD, and we have created many more disciples through their experience at DPL. We believe it is possible for most software developers to be good designers — they just need to put in the work to learn the craft (see the “Falling Short on Training Software Developers” post for why education has failed developers). Eric alludes to this when he suggests people should first invest time in learning how to design software and refactoring (with tests).
This need for resources to learn how to design software is exactly why we put together our Software Design and Development Clinic which we feel is a great starting point for your journey. Developers can be set up for success with a programming model that is easy to grasp and to apply. Many models, such as object-oriented analysis, fall short. A mindset of “design for change” and service orientation has consistently proven to be easier for our developers, customers, and interns.
In summary, we believe that developing tests alongside developing code that is built within a prescribed design based upon encapsulating change is the best scenario possible. We have demonstrated time and again that this yields the results that enable sustained velocity and business agility in our systems.
We realize this goes against the dogmatic incremental design crowd but we believe we must have a plan before starting to build the house — even if we decide to make design changes along the way during construction.