InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

Uncle Bob On The Applicability Of TDD

Posted by Mike Bria on Nov 04, 2009

Sections
Process & Practices,
Architecture & Design,
Development
Topics
Agile Techniques ,
Programming ,
Agile ,
.NET ,
Ruby ,
Java
Tags
Testing ,
TDD

Following up a pot-stirring blog where he asserted that "anyone who continues to think that TDD slows you down is living in the stone age", Bob Martin takes a stab at providing some deeper insight into the real applicability, role, and benefit of TDD.

He begins by taking on this big question: "Is TDD is a replacement for architecture?". His example-backed answer, 'no, BUT...':

The notion that you can generate a viable architecture by starting with a blank screen and then writing one test case after the other is sheer folderol. There are decisions that you need to make that have nothing to do with tests.

Of course many of these decisions can, and should, be deferred for as long as possible. For example, the database schema is something that can likely wait for quite a long time. The decision to use Spring, JSF, Hibernate, JPA, etc. can also likely wait. The beauty of business rules is that they can, and should, be implemented independently of database and GUI models.
...
Here’s the bottom line. You cannot derive a complete architecture with TDD. TDD can inform some of your architectural decisions, but you cannot begin a project without an architectural vision. So some up front architecture is necessary. One of the most important up front architectural activities is deciding which architectural elements can be deferred and which cannot.

Having answered the architecture question, Martin moves on to tackle the next logical topic: "Is TDD a replacement for design?". The essence of his answer being this:

No. You still need all your design skills. You still need to know design principles, and design patterns. You should know UML. And, yes, you should create lightweight models of your proposed software designs.
...
The bottom line is that TDD is a design technique but should not be the sole design technique. All the old design rules and skills still apply; and TDD is a powerful way to inform and augment them.

Tying back to another statement from his "stone age" blog, Martin posits himself the question of "Should TDD be used for every line of code?". Again, the answer is "no":

No. There is a set of problems for which TDD is not particularly helpful. GUIs are an example.
...
Of course it’s not just GUIs. It is the notion of fiddling that is the key. If you must massage the code into place. If you must fiddle with some aspect in order to please the customer. If there is some uncertainty that can only be resolved by a very rapid cycle of edit-and-run, then TDD is likely to be more of a hindrance than a help.
...
The trick to manage this is intense decoupling. You want to make sure you identify every bit of the code that does not need to be fiddled, and separate that code into modules that you can write with TDD. Make sure that the fiddled code is isolated and kept to a bare minimum.

Having conceded that some tests are in fact better written after, Martin goes on to re-iterate that should be done only when necessary (when "fiddling" is required). He states the primary reason behind this as that "it greatly enhances the chances that every line and every decision is tested", explaining how even the most disciplined programmers are bound to write some degree of un-testable code if the tests aren't written first.

Uncle Bob then poses this interesting question: "Given that we accept the need for tests, why the resistance to test-first?". To this, he posits the hypothesis that some people just aren't able to think through code incrementally:

Honestly, I don’t know [why there is such a high resistance to test-first]. Clearly it can’t be a productivity issue since we are going to write the tests anyway.

Perhaps some people don’t like the fact that writing tests first interrupts the flow. It’s true, when you write tests first, you cannot write a whole algorithm. You have to assemble that algorithm bit by bit as you add one test case after another. Maybe some people just don’t feel comfortable working this way.

Martin's final remarks give response to this common statement: "Wouldn’t it be faster without [having to worry about] such high test coverage?". First he concedes that getting high coverage in place for a legacy environment (one where the code does not have tests) does require a potentially high, long-term investment. In non-legacy environment and for new code within a legacy environment though, his answer is much different; in this case, high automated test coverage speeds you up. His reasons why:

Firstly, you don’t do much debugging. How could you if you have tested virtually every line of code? My own experience with debug time is that it all but disappears. In the last year of intense development effort on FitNesse I have spent almost no time debugging. If I had to quantify that time, I’d put it at 5 hours or less.

Secondly, I simply cannot inadvertently break the code. The test suite finds such breakage within seconds! And this makes me fearless. When you are fearless, you can go a lot faster.

Thirdly, My tests are little examples of how to work the system. Whenever I forget how some part of the system works, I read the tests. They quickly get me back up to speed.

Fourthly, I’m not fighting a continuous barrage of bugs from the field. Even though I have thousands of users, my bug list is tiny. The time I spend in support is less than an hour a week, and usually that’s just pointing people at the right spot in the user guide.

Check out Bob's blog for more detail and concrete examples of these ideas, and be sure to also take a moment to read through the immense amount of feedback and additional nuggets posted in the comments.

Related Sponsor

In today’s hyper-competitive world, later may be too late to adopt Agile development and this Roadmap for Success will help you get started. Download "Agile Development: A Manager's Roadmap for Success" now!

Where TDD makes you go faster by Mark Schumann Posted
Re: Where TDD makes you go faster by Sebastian Kübeck Posted
Re: Where TDD makes you go faster by Mike Bria Posted
Re: Where TDD makes you go faster, and fearlessly by Mark Schumann Posted
Re: Where TDD makes you go faster by Mark Levison Posted
  1. Back to top

    Where TDD makes you go faster

    by Mark Schumann

    I don't know. Maybe I'm abusing the TDD concept by using it in this way. But I am currently looking at some heavily coupled legacy code. As I make changes, I'm often stymied by the poor structure. I'm not sure what the arguments to a method really mean, or what is expected.

    When I get really stuck and frustrated, I'm adding unit tests that speculate as to how a method should work. When the test fails, I know my understanding of the legacy code was insufficient. When the test (eventually) succeeds, I build up the knowledge base that's embedded in the tests.

    And either way, I get off the path of frustration.

    So that's one way at least.

  2. Back to top

    Re: Where TDD makes you go faster

    by Sebastian Kübeck

    > When I get really stuck and frustrated, I'm adding unit tests that speculate as
    > to how a method should work. When the test fails, I know my understanding of
    > the legacy code was insufficient. When the test (eventually) succeeds, I build
    > up the knowledge base that's embedded in the tests.

    That's a great idea! It is called Explorative Testing, isn't it?

  3. Back to top

    Re: Where TDD makes you go faster

    by Mike Bria

    Sebastian is correct, this is called "explorative testing" (not to be confused with "exploratory testing").

    This is a technique I often use (and teach) when faced with some legacy code. The old chicken and egg though: I want to understand some legacy code. Refactoring to make it clearer would be great, but I need some tests to do so safely. Getting some tests around it requires understanding, which is where I started!

    But, as you describe, my flow usually goes: explorative test to get at least a few happy path tests in place, do some refactoring, get more tests, more refactoring, yada yada.

  4. Back to top

    Re: Where TDD makes you go faster, and fearlessly

    by Mark Schumann

    Thank you for your followups. I noticed something else upon rereading your summary, Mike, where Robert Martin talks about a good test suite making him "fearless." I recently blogged about fear in software development and agree that it's a huge issue. After decades of being afraid (often due to client or employer pressure) to change code that I didn't absolutely have to, due to poor understanding, the idea of having tests as a safety net is incredibly appealing.

    Well done. Thank you!

  5. Back to top

    Re: Where TDD makes you go faster

    by Mark Levison

    As others have noted Explorative Testing can be very useful. One key point is recognize when these tests lose their value and need to be thrown away. Sometimes when your doing this style of test you discover an unintended behaviour. Its important not to codify that behaviour as tested and therefore good. Instead root it out and destroy it.

    Cheers
    Mark Levison
    The Agile Consortium.

Educational Content

Jesper Boeg on Priming Kanban

In this interview, Jesper Boeg, author of the new InfoQ book – Priming Kanban, discusses the keys to using Kanban effectively, and how to get started if you are currently using other approaches.

New-age Transactional Systems - Not Your Grandpa's OLTP

John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.

Cool Code

Kevlin Henney examines code samples to see what can be learned from them starting from the premise that one won’t write great code unless he knows how to read it.

Collaboration: At the Extremities of Extreme

Jason Ayers share the observations he made watching a team of developers collaborating in real time on the same code base, pushing XP, pair programming and continuous integration to their extremes.

Yesod Web Framework

Michael Snoyman presents Yesod, a web framework written in Haskell and containing a web server, templating, ORM, libraries (templating, gravatar, etc.).

Transactions without Transactions

Richard Kreuter and Kyle Banker on how to avoid classical RDBMS transactional systems by using compensation mechanisms, transactional messaging or transactional procedures.

Attila Szegedi on JVM and GC Performance Tuning at Twitter

Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.

10 tips on how to prevent business value risk

One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.