TDD by Example: Chapter 2 Degenerate Objects

Summary

The chapter starts off with a summary of the "general TDD cycle". The summary of the summary is:

  1. Write a test that would pass if your code did what you wanted to do in the way you want to it do it.
  2. Get the test to pass as quickly as possible without regard to "clean code" or "design". Be shameless.
  3. Improve the design of the code while keeping the tests green. Clean up the filth and shame introduced in step 2.

He explains that the idea behind these three steps is to reduce the cognitive load of the programmer. First get the code to work, then worry about how it's designed. This approach is in contrast to what he calls the "architecture-driven development" which focuses on the design of the code first and then tries to get it to work. This is what makes TDD a design tool.

He then puts the idea into practice removing the side effects from the Dollar object that mutates the value if a multiplication operation is performed. He writes a test that would pass if multiplying a Dollar object returned a new instance of Dollar whose amount is the result of the multiplication instead of mutating the amount of the old instance.

This is followed up by a discussion of two tactics used to quickly get to passing tests. The first tactic is to hardcode something and then gradually put in real code while keeping the test passing. The second tactic is to do the real implementation if it is obvious. These are the two main strategies used by Beck. He mentions a third tactic, Triangulation, that will be explained in the following chapter.

There is the usual summary of the chapter in bullet points and then an extra paragraph talking about using your feelings to guide you. Beck used the feeling of something being wrong with the code to write a test that exposes the reason for the feeling. Getting the test to pass made him feel better and improved the design.

Commentary

There are some great things about this chapter and some things I find problematic.

I think the restatement of the TDD cycle and the example that followed is great. Repetition of the steps of TDD in a different way helped me deepen my understanding of it. I hope he does this many more times throughout the book.

The goal is clean code that works (thanks to Ron Jeffries for this pithy summary). Clean code that works is out of the reach of even the best programmers some of the time, and out of the reach of most programmers (like me) most of the time. Divide and conquer, baby. First we'll solve the “that works” part of the problem. Then we'll solve the “clean code” part.

This quote captures the power of TDD as a design tool. By writing the test, getting it to past and then worrying about the design of the code, you can be sure that your design works.

The problems start right after:

This is the opposite of architecture-driven development, where you solve “clean code” first, then scramble around trying to integrate into the design the things you learn as you solve the “that works” problem.

I think this is a straw man. There are problems that require you draw a diagram, to separate out the parts of the before you write a line of code or a test. Drawing a sequence or class diagram can help you understand where you should write your first test. It helps to think through the problem at a different level.

I've experience problems that are too big to just allow to to emerge completely by TDD. When designing a large system, we usually have to do some upfront design to decide where certain parts go and how they will interact with each other. Requirements strongly influence the design, not just the functional one that users see but the non-functional ones like availability and resiliency. TDD is a great design tool but it's not the only one.

The other thing I have a problem is this:

The translation of a feeling (for example, disgust at side effects) into a test (for example, multiply the same Dollar twice) is a common theme of TDD.

When he was writing this book Beck was already an expert at TDD. He had been doing it a long enough time to have built up an intuition. He could feel when some piece of code needed to be re-design. A person just learning will not have those feelings. I think it would have been a more useful tool for a wider range of people if he took the time to point exactly what was wrong with the design. It takes a lot of practice to get a feel for good design.