Another follow up to
’ interview of Prof. John Ousterhout.I disagree (not surprisingly) with his take on TDD:
Yeah, I'm not a fan of TDD because I think it works against design. So again, to me, tests are important. I love unit tests. I write them for everything I do. They're essential. If you're a responsible developer, you write unit tests with very high coverage. So let's agree on that.
But we want the development process to be focused on design. I think that should be the center of everything we do in development should be organized towards getting the best possible design. And I think TDD works against that because it encourages you to do a little tiny increment of design.
First I want to focus on “TDD works against design”. Prof. Ousterhout seems to be saying that there is little or no room in TDD for design. It’s as if TDD looks like this:
That’s it. We have a design, we write a test, we make it work however, then immediately write another test.
When I apply TDD, however, I have several points that remind me to design:
After I get a test running, I think about, and sometimes execute on, the question, “What is the design which, if I had had it, would have made that implementation easier?”
When I go to write a new test, I necessarily make API design decisions. (Prof. Ousterhout doesn’t talk much about the distinction between interface & implementation design that I’ve seen.) If the existing API proves awkward, I can refine it now.
“Little tiny increment of design”
Guilty as charged. Why wouldn’t you grow the structure like a tree grows? Reasons I can think of:
Local maxima. You grow to a design that turns out to be undesirable & you can’t find a walkable path to the design you need.
Pressure. Once you have a running system, the pressure for the next feature & the next feature may prove so great that you begin to skimp on design.
Vision. Once you have behavior in place, the complexity may prevent you from seeing a more-desirable design.
Those are just my reasons. Prof. Ousterhout doesn’t explain further.
I agree that the above are challenges to an incremental style. There are skills to be learned to deal with local maxima, feature pressure, & maintaining vision. However, the incremental style also brings benefits:
Learning. The earlier you make design decisions, the less informed you are when you make those decisions. The more you need to learn, the later you should design.
Adaptation. Early decisions necessarily rest on assumptions about what features, what form of optionality, will be important. The more the features flex from your original vision, the later you should design.
Timing. The sooner you see features, the sooner you can validate the value & desirability of those features. The greater the cost of delay, the later you should design.
Notice that I say “later”. The question is about when, not whether. A tradeoff space should shape how much you design & when.
Balance > Best possible
the center of everything we do in development should be organized towards getting the best possible design
Disagree. And this is probably approaching the heart of our disagreement. As an engineer I love love love when a design clicks into place. I love the feeling of possibility as I implement feature after feature with no unnecessary friction. I love deleting cruft & waste. However…
I know that I can go too far. The structure world is a world of possibility. Of options. Some of those options need to be exercised or the project will die, run out of money, of energy, of runway.
And the alternative, designing like a tree designs, growing like a tree grows, is not so bad. I still get those moments of large-scale pattern recognition that thrill my heart. I get to solve the puzzle of getting from here to there in small, safe steps. My colleagues & I share the benefits of a good design where it needs to be good.
Note, John does confess that he himself has not tried TDD flow, hes only seen some examples of people/students who think they have tried TDD. If my implementation of a practice is flawed, it's not to be taken as a reflection of the practice itself. Regardless of whether or not one practices TDD, software design is matter of discipline. If I have a good discipline for design, I can do it with TDD as well (or in John's case despite TDD 😀)
"Yeah, I'm not a fan of TDD because I think it works against design."
One must use a single, consistent paradigm or technique for design. That feels like a faulty assumption to me.
It's similar to the notion that we need to use one programming paradigm.
I really like having different design tools to apply to different contexts. Some examples:
I love TDD for core domains, not so much for writing front-end code. In the later, I prefer (keyword) a more designer (the role) driven process. Test-centered, of course, but not strictly test-driven.
Or when I encounter a codebase that wasn't built with TDD, I like using the C4 model to capture the current state and its dynamic diagrams to plan extractions or large-scale refactorings before introducing behaviors, likely using TDD. So, top-down until I can get to a place where emergent design works.