How much design before unit testing, and how much design knowledge before TDD?

In my Third Law of TDD Liang asked me in a comment:


“Could you let me know if you have a basic application architecture design in mind before you write the first line of code? Or you just let the test/code lead you to a design. For example, if you find a common method in related classes, you create a Interface, just as you mentioned in validation. If that is correct, I think TDD is pretty hard for a beginner, since the developers should have very good knowledge in OO, design patterns, refactoring before beginning implementing TDD. otherwise how she/he know how and when to refactoring?…”

To the first question, do I have a basic application architecture in mind before I write the first line of code?  That’s a long conversation, but the short answer is that I generally have a basic idea for the application/system/service as a whole.  The longer answer that I’ve learned over the last couple years is to not commit to a design or a structure upfront.  As far as the unit tests leading the design and knowing when to refactor, you should constantly be queuing up design ideas and evolving your thinking about the design but the unit tests drive the actual direction.  You should only introduce an abstraction, either upfront or by a refactoring, when you know enough to say that the abstraction in question is valuable.  Creating an abstraction too early often leads to painful, sclerotic code.  Waiting too long is a wasted opportunity or a harder refactoring.  The key is to make design decisions of all kinds at the Last Responsible Moment.  Determining or recognizing that last moment is something that comes with experience.


To the second question, how much should you know before you start using TDD?  I’d restate that question to a more generalized “how much do you need to know to start programming?”  While TDD does have some of its own lore (xUnit, mocks, Dependency Injection, IoC, etc.), for the most part the knowledge and skills that enable effective TDD are the same skills you would want to do more traditional upfront design work.  You’ve got to look for a coding and design style that fits with your own thinking style to balance upfront thinking and reflective thinking.


I would, and do, recommend TDD for new developers.  You have to start at some point and it certainly helps to get some coding experience under your belt before you tackle subjects like Design Patterns and Code Smells.  I think the value of TDD for a new developer is the concentration on verifying code in small chunks for the rapid feedback.  Another advantage of using TDD as a beginner is the emphasis on simplicity as you layer on functionality little by little and introduce abstractions slowly. 


You simply can’t ever code blindly without thinking for very long, so you might as well get used to working reflectively.  Many of the worst application architectures I’ve seen (and one that I did) were a result of determining abstractions upfront that didn’t really work, and then compounding the problem by bludgeoning blindly ahead.  Regardless of your style, you absolutely have to continuously reflect on your design as you work, both to spot design flaws and opportunities for better designs.


To be honest, I think TDD is more difficult for someone with a lot of experience in upfront design techniques.  I had to “unlearn” quite a bit before I became effective with TDD.  The developers I’ve worked with that started in Agile shops seem to have an easier time within an Agile process.


As far as what you need to know to be effective with TDD and continuous design in general, the more you know the better you’ll be.  I’d start with basic OOP best practices with something like the Craig Larman book.  Learn about code smells and Anti-Patterns to recognize when your code is moving in a bad direction.  Studying design patterns will help you inside of any design technique or development process.


Here’s two absolute truths about an individual’s coding:



  1. Everybody works differently

  2. Results will improve with more experience and more knowledge

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Design Patterns, Test Driven Development. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/blogs/jeremy.miller jmiller

    Bill,

    Thanks for the comment.

    In terms of designing user interface code in .Net (especially with WinForms), yes, I almost automatically reach for an MVP pattern now. The *details* of the MVP implementation can easily vary from application to application though. I bet there is an exception case, but I haven’t hit it yet.

    For web development, there might be better answers. I’m awfully close to joining the anti-webforms brigade and pushing for MonoRail or a different platform altogether the next time I’m back in a web development project.

    Back to the idea of “Last Responsible Moment.” An MVP architecture has to be started relatively quickly, or you’re in for some difficult refactoring. You know from studying the MVP patterns that it enhances testability and maintainability. It is responsible to use MVP upfront.

    I’m going to blog on this again soon, but this is yet another example of using design patterns as a shortcut through the design process.

  • Bill Campbell

    Hi Jeremy,

    Excellent post – I really enjoy all your work – thanks for sharing so much with the development community!

    I work in a .net development world for now and when I think about this whole idea of the up-front design, isn’t there one part of this design that pops out as almost a requirement, and that is MVP? If you’re not going to start with MVP, then how would you even go about using TDD to develop an application? I mean, you could use TDD for your middletier and down, but it seems like if you don’t start with a commitment to the MVP pattern, a lot of what you do will be untestable as it will fall into the code behind. Please share any thoughts you might have about this.

    regards,
    Bill

  • jmiller

    David,

    I don’t have anything, but you might try Bob Martin’s Bowling game or prime number exercises here –> http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

    as a “Hello World.”

    Jeremy

  • http://pah2.golding.id.au/ David Golding

    Do you have (or what would you recommend as) a Hello World for TDD?

  • Liang

    That is great answer! Very appreciate, Jeremy! I think a lot of developers will benefit from this post.