Your Design Is Broken: It’s TDD, not TDYAR

Think about your last huge unrequited crush.

A real one, I’m talking. A crush so big you go to sleep thinking of your future together, and wake up, ummm, guilty. The kind of crush that great romantic stories are made from, and for. The wobbly-knee’d kind.

And a complete non-starter: when I say unrequited, I’m being clinical in an effort to minimize the likelihood of breaking out into laughter, as doctors and nurses in every ER around the world do.

Now, in that kind of crush, you have a vision, and that vision is glorious beyond compare. It captures entirely your ideal notions of successful love. I’m not going to ask you to describe it. In fact, please don’t describe it. I have sensitive comment readers here. But picture that imagined future, just for yourself.

Was it magnificent? Brilliant? A tale for the ages?

I’d bet it was all this and more.

In fact, there’s really only a single flaw in that entire vision.

That Future Romance Is In Your Head

That tremendous romantic vision is just that, a vision. And when it came time for that blueprint-for-love to hit the shredder we call real life, it just wasn’t up to the challenge.

Thus with your design.

You see, your design lives in your mind, like your romantic vision. Oh, it may have slightly more contact with reality, because, after all, I still can’t believe you thought that — never mind.

And, notice, I’m not suggesting you’re not the greatest software designer in known space. My point in no way depends on you being a sub-par designer. Surely, you’re not a sub-par designer of romantic visions for you, are you? Far from it.

Codeless Designs Are Relatively Worthless

A design in your head elides all messy details, tricky complications, and inevitable exceptions.

A problem your company wants to solve never does.

So how do you cope with this divergence?

You code up your design, and as the working code begins to implement the design in your head, you keep an eagle-like watch out for its inevitable infelicities, and you change the code — and your design — until it is still working and no longer has its problems.

That’s what TDD does. A sketch of a design leads to rolling some microtested working code which is re-designed in microsteps. Red. Green. Refactor. Commit.

Design-In-Head Vs. Design-In-Working-Code

When your starting design meets the emerging design that comes from repeatedly applying the TDD process, there will be clashes. Thankfully, most of them aren’t as bad as that great unrequited love. But, on the record, some of them are.

When it happens, which design is right?

The one in the working refactored microtested code is right.

Your Design Is Broken

That’s hard, but you have to let it go.

Use a mental design to work your way into the code using TDD, and your broken design will become a masterwork of productivity. Ignoring the code’s design in favor of the mind’s, and you will pay a great price, starting tomorrow and lasting until your company inevitably goes out of business.

People rail against TDD.

A great many of them are refusing to confront this basic clash of designs, and they just can’t let go of their mental construct in favor of their silicon one. (The most recent such, from the famous @DHH, reeks to me of this.)

And that explains for me a large amount of TDD criticism, and an even larger number of failed TDD adoption efforts.

If you think TDD is supposed to work w/o changing the shape of your designs, you have missed the point. It is not surprising to me that you find yourself hating TDD.

I see a lot of “TDD and my design clash, therefore TDD doesn’t work”. I’ve seen even more of it lately.

TDD Is Test Driven Development,


Test Driven “You Are Right”

10 thoughts on “Your Design Is Broken: It’s TDD, not TDYAR

  1. Thanks for this post. It’s *so good*. I had very similar thoughts about the subject. I had caught myself imagining how things should be designed or even specifically implemented *before* writing first test and that, in my opinion, is already a lost TDD game. I’ve stopped doing that and I immediately noticed a great improvement in both design of the code I write *and* productivity. Simply because it allowed me to always focus on the things that really matter instead of building this mental model in my head upfront, or even parts of it, that’s a distraction, it slows you down.

    • This thing about mental scope — keeping the focus on the exact important thing — has to be my next target. Then there’s the whole question of how to *see* the implicit design when the code is showing what it should it be. Anyway, I’m glad it’s working for you. I’ll tell you, that’s exactly how it works for me, too.

  2. “Ignoring the code’s design in favor of the mind’s, and you will pay a great price (..)”
    Here, I did! I already did it once. It was painful. It hurts me ’till today. Wondering a perfect code-world, every message talking in harmony with others – but I forgot the main target: “code’s design”. In the end it went almost as described by you: i need let it go. Move forward. Today I never do that thing, but I should live this to learn.

    Thanks for great post – in this new point of view.

  3. LOL – the “design in your head” connecting with the “shredder we call real life” reminds me of Kent Beck’s “Coding is when your fuzzy, comfortable ideas awaken in the harsh dawn of reality”

  4. I have a question.

    I’m “trying” TDD for at least 4 years now and I am still no master, although I know many techniques and can apply them accordingly. I am still a skeptic I must admit, although I already earned value in various situations.

    As I understand your post, you still have this “high-level” visions first and try to implement and test them step-by-step. On the way you adapt this high-level ideas to code reality.

    So here comes the question.

    My largest “issue” with TDD is that when you write test code you can get distracted by details that have nothing to do with the design you solve, like mocking dependencies, creating boilerplate test data. It’s really hard to not get stuck into details and to not forget the high-level ideas (of your new design).

    Also a list of test cases in code, even with good naming patterns isn’t so expressive like a mind map or a good technical specification. Sure this is high level things are incomplete and sometimes wrong, but they are still valuable, aren’t they?

    How do you deal with that “focus” and “envisioning” thing? Do you “think” in the code editor only?

    • Hello, Andreas. I understand your frustration. The pain you feel in both mocking collaborators and generating test data embody Mike’s point: TDD practitioners assume that this pain points to a problem in the (production code) design, and not a problem in the tests. Of course, we can’t always fault the design, but we look there first for opportunities to improve things. I’ve written about this before regarding collaborators: complicated mocks indicate complicated interactions which typically merit improving. With test data, I don’t see it quite so clearly. Sometimes we have complicated data structures because we have complicated problems; sometimes we have them because we’ve put too many things in one place. I like that my tests encourage me to think about which data to consider essential and which to consider incidental. This can help me make better user interface choices. (Of course, in a legacy system, where we have less authority to change things, this extra insight infuriates me more often than it helps me.)

      When I program, I bounce back and forth among code, test lists (on paper or in code) and diagrams. I couldn’t imagine doing it any other way. I do one thing differently than I did 15 years ago: I prefer to put the most hideous details *only* in code, because only the code absolutely needs them. I stop drawing a diagram when I understand the details of what I want to build next, rather than trying to make the diagram perfect before writing code. I use test lists to get ideas out of my head (so as not to forget them) rather than to try to plan in detail what I will do. Only the code forces me to express every last detail, so I save my energy to express every last detail in the code. (I treat the tests as part of the “the code” here.)

      Only “the code” *has* to exist. Only “the code” *has* to express correctly what we want. It seems perfectly reasonable to me to therefore focus on doing our best, most accurate, most thorough work in the code. I wouldn’t want to write technical specifications, write test lists, draw diagrams, nor talk to others about the work, if not for the goal of getting the code right. For me, whatever you need to clarify your intent when you write code, you ought absolutely to do.

  5. The main thing that I learned from the discussions between Kent Beck, Martin Fowler, and DHH that followed that blog post from DHH is that the difference of opinion is not actually that much:

    At one point DHH said he would be happy with as low as 50% code coverage – well if we are just getting cranky over which code coverage % is best overall, then we are actually a very long way from claiming it is dead. Most developers that I know use TDD at least most of the time.

  6. I still don t get how TDD can work how do you develop your tests before you develop your tests? it is a lame concept for lame programmers who want to pull the wool over the eyes of non-technical types.

    • Mariano…

      1. I feel ya. I really do.

      2. TDD has many markedly counter-intuitive aspects to begin with. To make matters worse, there’s a ton of advocacy out there that tries to make hard things easy, that tries to close deals, that tries to make someone internet-famous, and that tries to suggest that this or that formula can replace human judgment. That, not to put too fine a point on it, seems like scamming to me, too.

      3. I am not a scammer, and I am not lame. I am a strong geek and a strong coach of geeks, and I have been both for a long time.

      4. Like most folks, I have to balance funding needs and interests and my learning processes, and things go very slowly, but I can tell you that I am hard at work out there trying to change this situation, with respect to both TDD and several other ideas associated with agile practice, and most importantly, how they are spread through the trade.

      TDD doesn’t work by writing tests then writing code. It works by writing *one* test and solving it, then another, and solving it, and so on. It doesn’t work (well) by making those tests whole huge system tests. It works mostly by letting what you learn from one small test inform your ongoing design.

      Thanks for checking in — GeePaw

      P.S. Take a look at other articles here for some ideas. Also, you might be interested in this series from 2010:

Leave a Reply

Your email address will not be published. Required fields are marked *