Recently I tweeted that TDD can’t lead to a good design if we don’t know what good design looks like. I was also saying that we probably should teach design before TDD (or at least, at the same time). This tweet led to a discussions with J.B. Rainsberger, Ron Jeffries, and a few others. J.B. and I ended up having a live discussion on Hangout on Air later on.
If you look back to many of my talks, blogs, and even my book, you will find multiple occasions where I say that TDD is a design tool. So what changed? Why I don’t say the same thing anymore?
After paying more attention to how I work and how many other developers work, I realised that not many people are driving good design through TDD. Although I love the RED-GREEN-REFACTORING rhythm, having a “refactoring” step is not enough to call TDD a design tool.
TDD doesn’t prescribe how you should design. What it does is to annoy you constantly, asking “Are you sure about this? Is it good enough? Can you make it better?” This annoyance (or constant reminder to look at your design and thing if it can be improved) is a great thing, but not enough.
In my view, TDD is a software development workflow which provides me with many benefits, including a constant reminder to make my code better. What it means to make my code better, is not part of TDD.
Ah, yes… But no. I’m not forgetting about them. 4 Rules of Simple Design are NOT part of TDD and I’m purely discussing TDD here. 4 Rules of Simple Design is normally the design guidelines that many experienced TDD practitioners use (including myself, among other techniques) during the refactoring phase.
4 Rules of Simple Design is one of the many design guidelines we have available. SOLID is another. Domain-Driven Design is another. Many other design principles and patterns are also available as good guidelines. Those are the things we need to have in our mind during “refactoring” phase. Or, putting it in a different way, having a good understanding of the existing design guidelines is what will lead you to a better design.
TDD is a workflow (not a design tool) where during the refactoring phase you apply your existing knowledge of software design combined with design techniques that may help you to get to a better design.
There are two main styles of TDD with significant differences between them, mainly when it comes to design.
The Classicist approach is the original approach to TDD created by Kent Beck. It’s also known as Detroit School of TDD.
Outside-In TDD, also known as London School or mockist, is a TDD style developed and adopted by some of the first XP practitioners in London. It later inspired the creation of BDD.
Both. All. They are just tools and as such, they should be used according to your needs. Experienced TDD practitioners jump from one style to another without ever worrying which style they are using.
There are two types of design: macro and micro design. Micro design is what we do while test driving code, mainly using the classicist approach. Macro design goes beyond the feature we are implementing. It’s about how we model our domain at a much higher level, how we split our application, layers, services, etc. Macro design helps us with the overall organisation of the application and provides ways for teams and developers to work in parallel without stepping on each other toes. Macro design refers to how the business sees the application and techniques like Domain-Driven Design are commonly used. Macro design also helps with consistency throughout the application. TDD won’t help you with macro design.
Macro design is normally taken into account when using Outside-In TDD, but Outside-In on its own is not enough to define the macro design of an application.
Over the years I’ve seen many applications that have been test-driven and were still a pain to work with. OK, I admit that they were significantly better than the majority of the legacy applications which had no tests that I had to maintain before that.
Any developer can make a mess regardless if they are writing tests or not. Developers can also test drive crap regardless of which TDD style they are using.
TDD is not a design tool. It’s a software development workflow that has prompts for code improvement in its lifecycle. In these prompts (writing tests and refactoring), developers need to know some design guidelines (4 Rules of Simple Design, Domain Driven Design, SOLID, Patterns, Law of Demeter, Tell, Don’t Ask, POLA/S, Design by Contract, Feature Envy, cohesion, coupling, Balanced Abstraction Principle, etc) in order to make their code better. Just saying refactoring isn’t enough to call TDD a design tool.
Many developers blame TDD and mocks for slowing them down. They end up giving up on TDD because they struggle to get the result they want. In my opinion, no developer really struggles to understand the RED-GREEN-REFACTOR lifecycle. What they struggle with is how to design software well.
The great thing about TDD is that it is constantly asking us “Hey, can you make your code better? See how hard testing this class is becoming? OK, you made it work. Here’s your green bar. Now make it better.” Besides that, you are on your own.
TDD becomes much easier when we understand what good design looks like. Practicing and understanding the wealth of design guidelines available will make TDD much easier and useful. It will also reduce its learning curve and hopefully increase its adoption.
Extremes are bad. We are going from BDUF (Big Design Up Front) to no design at all. Throwing away our design knowledge is a mistake. Sure, we should not go back to the dark ages and over-engineer everything but thinking that we should only focus on micro design is also a mistake. If you are working on your own, doing a few katas, or working on a small application, then yes, do whatever you like. But if you are part of bigger team developing something that is significantly bigger than a kata, you will be doing your team a favour if you paid more attention to macro design and how you structure your code.
Software is our passion.
We are software craftspeople. We build well-crafted software for our clients, we help developers to get better at their craft through training, coaching and mentoring, and we help companies get better at delivering software.