views:

115

answers:

3

Similar to http://stackoverflow.com/questions/2149369/does-tdd-mean-not-thinking-about-class-design, I am having trouble thinking about where the traditional 'design' stage fits into TDD.

According to the Bowling Game Kata (the 'conversation' version, whose link escapes me at the moment) TDD appears to ignore design decisions made early on (discard the frame object, roll object, etc). I can see in that example it being a good idea to follow the tests and ignore your initial design thoughts, but in bigger projects or ones where you want to leave an opening for expansion / customisation, wouldn't it be better to put things in that you don't have a test for or don't have a need for immediately in order to avoid time-consuming rewrites later?

In short - how much design is too much when doing TDD, and how much should I be following that design as I write tests and the code to pass them (ignoring my design to only worry about passing tests)?

Or am I worrying about nothing, and code written simply to follow tests is not (in practice) difficult to rewrite or refactor if you're painted into a corner? Alternatively, am I missing the point and that I should be expecting to rewrite portions of the code when I come to test a new section of functionality?

+6  A: 

I would base your tests on your initial design. In many ways TDD is a discovery process. You can expect to either confirm your early design choices or find that there are better choices you can make. Do as much upfront design as you are comfortable with. Some like to fly by the seat of the chairs doing high level design and using TDD to flesh the design out. While others like to have everything on paper first.

Part of TDD is refactoring.

dr
+1 for 'Part of TDD is refactoring': The design will need to be updated to reflect the changes found necessary while testing.
SnOrfus
Thanks then - so I am missing the point, and the refactoring / rewriting is all part of this emergent design. Good to know, now to try it out with something!
pete the pagan-gerbil
+2  A: 

Start with a rough design idea, pick a first test and start coding, going green test after test, letting the design emerge, similar or not to the initial design. How much initial design depends on the problem complexity.

One must be attentive and listen to and sniff the code, to detect refactoring opportunities and code smells.

Strictly following TDD and the SOLID principles will bring code clean, testable and flexible, so that it can be easily refactored, leveraging on the unit tests as scaffolding to prevent regression.

philippe
So the initial design is more there in case the emergent design runs into a brick wall (or looks as if it's about to)?
pete the pagan-gerbil
The initial design is to get started, to give the first direction, for the problem - or a subpart of the problem. The design is created incrementally.
philippe
+1  A: 

I've found three ways of doing design with TDD:

  • Allow the design to emerge naturally as duplication and complexity is removed
  • Create a perfect design up-front, using mocks combined with the single responsibility principle
  • Be pragmatic about it.

Pragmatism seems to be the best choice most times, so here's what I do. If I know that a particular pattern will suit my problem very well (for instance, MVC) I'll go straight for the mocks and assume it works. Otherwise, if the design is less clear, I'll allow it to emerge.

The cross-over point at which I feel the need to refactor an emergent design is the point at which it stops being easy to change. If a piece of code isn't perfectly designed, but another dev coming across it could easily refactor it themselves, it's good enough. If the code is becoming so complex that it stops being obvious to another dev, it's time to refactor it.

I like Real Options, and refactoring something to perfection feels to me like committing to the design without any real need to do so. I refactor to "good enough" instead; that way if my design proves itself to be wrong I've not wasted the time. Assume that your design will be wrong if you've never used it before in a similar context.

This also lets me get my code out much more quickly than if it were perfect. Having said that, it was my attempts to make the code perfect that taught me where the line was!

Lunivore