TDD design trade-offs and junk food

Tony Rasa recently talked about design trade-offs when doing TDD:

When “doing TDD,” we consciously make design trade-offs to favor testability. … we end up with a lot of single-implementation interfaces because of testability concerns and from weaknesses with our tools.

When I first started doing TDD, many of the new designs that came out of this practice seemed backwards and counterproductive at first.  After all, I wound up with many more classes, lots of interfaces, and most interfaces had exactly one implementation.

It turned out that before doing TDD, I wasn’t very good at OO design (not that I’m all that great now).  I tried, but all in all it was largely a bunch of guesswork coupled with the fear of changing code.

In college, I decided to give up caffeine, fast food and soft drinks.  Initially, healthy food tasted disgusting and it felt like I was going through junk food withdrawal.  After a month or so of following a healthier diet practice, I decided to splurge on a cheeseburger.  It was disgusting, and I was thoroughly nauseated.  My body was not used to such high levels of fat, and was letting me know it was not pleased.

Starting TDD is giving up OO junk food.  No more short cuts, god classes or big balls of mud, which seemed so comforting before.  No more wallowing in the debugger, taking pride in knowing how to take apart the Gordian knot, or making a mental stack of variables.  It hurt at first, seemed a little alien, but led toward leaner, tighter and more cohesive designs.

As for these so-called “trade-offs”, they are the first steps towards better designs.  One-implementation interfaces aren’t a design smell, it’s separation of concerns in action, dependency inversion principle in action.  Breaking big pieces into smaller cohesive units works on any scale, and you’ll be hard pressed to find the lower limit of its cost.

Because my initial OO designs had next to zero interfaces, it seemed rather silly to have a bunch of seemingly one-off implementations.  Eventually, I saw interfaces for what they truly were: contracts.  An interface is a contract, that any implementation needs to adhere to.  Consumers of the interface do not care about implementation details, nor can they, as the interface provides no other information besides the signatures it provides.

When doing TDD in a top-down behavioral design fashion, I create interfaces well before I ever create an implementation for them.  And that’s reducing cost, as I don’t write code I don’t absolutely prove that I need yet.  TDD forces the developer to put down the cheeseburger, drop the chalupa and ditch the Mountain Dew, forming a leaner, tighter and more cohesive codebase.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in TDD. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • You’re reading my mind… get out of my head! :)

    seriously good stuff, Jimmy.

  • Well put. The transition to TDD is definitely a rocky one filled with a lot of self-doubt. It feels silly writing all the interfaces and ending up with a million files when you could have gotten by with only a few. It’s not until the time comes to modify the implementation that you truly appreciate TDD.

  • Well put,

    Many times i have had to have this exact type of conversation with people when they see my foo : Ifoo and could not understand why.

    It is really hard to get others to understand the ‘why’ behind never using the concrete object. I always use interfaces for params, return values, etc.

  • Hi,
    Great post! I also believe that the path to TDD is a rocky one but once you are at the top you can see the complete picture clearly. TDD acts as a safety net for your applications. If you change your design and it breaks something your tests will bang hard on your head forcing you to fix the problem.

  • My point is that the decision to use TDD and proper OO is a decision with consequences. One that happens to be worth it, in my opinion, but everything has a cost.

    [Interfaces happens to be one aspect of TDD-ish design that we've been discussing a lot lately, but you can pick whatever design consideration you wish.]

    You could also write a “successful” project completely procedurally, with lots of flow charts. Worth the trade off? No.

    Trying to get developers to look past the artifacts of TDD and see the value is something that I am still working on how to communicate effectively. Ultimately though I think people have to see it for themselves.

  • Love the Junk Food metaphor by the way :)

  • @Kevin

    It’s nice to be 6 months in to a project, have over 1000 tests and have that confidence to make large changes in the codebase. Tough to go back on that.

  • Amen :)

  • Well said. I didn’t like Mountain Dew anyway

  • I think that building better designed code is usually an unmentioned benefit of TDD. I’ve been using TDD for around a year now, and my design and pattern usage has come on leaps and bounds. My team’s code is more stable (because of the continual testing) and easier to maintain and extend (because of the improved design)

  • If it can be of any helpful resource to someone, I wrote a post a while back about What TDD Has Taught Me So Far. I’m curious to know what this practice has taught you guys/gals so far as well.

  • Well put Jimmy! Well worth your 58K readers =)

    @Tony, probably the first and foremost benefit to TDD that I get across is the fact that you have a safety net when making changes to your code base. This in turn allows you to refactor your model when necessary rather than apply bandages (granted that your model is flexible enough), with minimal cost and damage.

  • @Sean

    Just wanted to point out, it’s 58000K. That’s 58 million readers. Eat your heart out Atwood!!!!1

  • Your description of where you were is exactly the spot that I am currently at. I think I am going through a phase of thrashing, of sorts, which is frustrating because I know where I want to be but getting all the pieces together, all at once, just isn’t quite there yet.

    However I do think that telling people to stop drinking Mt. Dew is just flat out irresponsible – what’s next no Tool or Deftones in the ear-buds? :)

  • Ok, but why are “contracts” important? I can write concrete classes that interact with each other just fine without interfaces. And as Roy has already demonstrated, you don’t need interfaces for test-focused SoC. To wit, any concrete class’s signature is a contract with its user.

  • MotoWilliams,

    > getting all the pieces together, all at once, just isn’t quite there yet

    Taking small steps (very small steps), and doing one thing at a time is a non-negotiable part of the TDD regime. You’re bound to spend more time learning if you don’t take these two tenants very seriously.

    I wasted a year on really fluffy TDD until I decided to take Kent Beck’s guidance seriously. When I realized JUST HOW SMALL of small steps TDD uses, and how serious is the admonition to do one thing at a time, things got better quite fast.

  • @Scott

    Re: why are contracts important:

    When reducing interaction to solely the contract, C# allows me to control the contract to fine-tune the interaction to exactly what the behavior should allow. The interface is just a reflection of the language’s lowest common denominator of an interaction definition.

    Interfaces helped move me beyond test-focused SoC that I started out with. Once I started looking at SRP at the interaction level, I feel like I started to understand what SRP was intending.

  • @bellware

    re: why are contracts important

    creating software that is understandable!

  • > The interface is just a reflection of the language’s lowest
    > common denominator of an interaction definition

    A method table address and binding is the lowest common denominator of an interaction definition. An interface is just one way that can be done in one programming paradigm. And by paradigm, I’m not talking about OO, but static binding – which is ultimately a pre-optimization technique more than it is a design concept.