This is my attempt at sharing some of my personal reasoning around the topic of test-first development.
Let me first sum up my thoughts in one shot:
At the class level, form and function are significantly more important than implementation.
Test-first development forces me to focus on the usage and external structure of the class before allowing me to create an implementation. Within the context of a specific behavior or piece of functionality, there are an infinite number of implementations which will work. Given the infinite number of ways to implement the piece of functionality as a class, it’s the class’ form that is the key differentiator. Specifically, each form exhibits a unique set of qualities. It’s by these qualities that each implementation can be judged.
Test-first development (as it was intended) forces me to focus on selecting the highest quality form from all designs that will meet the functional requirements. Implementation is easy. Design is tricky. I choose to let that which is most important drive my development process.
On the contrary, if the structure of the classes in your application are accidentally created as a side effect of their implementation, the system’s nonfunctional properties, such as maintainability, will also be accidentally affected. This is because the external structure of your classes are the knobs for such nonfunctional qualities as maintainability, modifiability, and testability.
Given the importance of these nonfunctional properties, this is to be avoided at all costs. Failure to meet nonfunctional requirements is the primary technical cause of catastrophic project and/or application failure.
Test-first development is a risk mitigation activity for key nonfunctional system properties and a defect-prevention process.
This ties heavily into my knowledge of system and class design–issues such as coupling and modularity. By preselecting the form and use of the classes in my design, it forces me to think up front about the nonfunctional knobs of this cog in my system–knobs such as coupling, testability, maintainability, and modifiability.
By preselecting the structure and functionality of the class, I constrain the implementation.
Gold-plated code is a bit of an epidemic in the software development industry. By constraining the implementation with form and usage, I become focused on implementing exactly what’s needed–nothing more.
Test-first development is neither a panacea nor a silver bullet.
It doesn’t guarantee I will select the best design. It doesn’t even mean my selected design is implementable. But if and when I recognize either case, I can quickly throw away the old design and start fresh.
Or at least that’s a side effect of having a maintainable, modifiable system.
It’s about having a tight, automated feedback loop on the health of the implementation.