0:15 I recently came across a critique of
0:17 test room development that I've heard
0:19 before and often but don't really
0:22 understand. I get stuff wrong all of the
0:24 time which is exactly why I prefer test
0:36 Hi, I'm Dave Farley of Continuous
0:38 Delivery. Welcome to my channel. If you
0:40 haven't been here before, please do hit
0:42 subscribe and if you enjoy the content
0:44 here today, hit like as well. The
0:47 critique that I came across was from Art
0:50 Zachenko. Here it is. In it, he says,
0:52 "Test room development implies writing
0:54 tests before writing the code. You write
0:56 tests to describe the intention behind
0:58 the system that you want to build, how
1:01 you expect it to behave. TDD heavily
1:04 implies that you should know how the
1:06 system behaves before even implementing
1:09 it. You should know what you're doing.
1:11 Most of the time you don't. All the
1:13 developers that I know don't know that
1:15 ahead of time. I don't know that ahead
1:18 of time. So yeah, okay. I can agree with
1:20 most of that. I don't think that I agree
1:22 with it in the way that meant though
1:25 because he goes on to say, "I don't see
1:27 test room development as a viable
1:29 approach in fast evolving systems like
1:33 the web." Well, first I confess I found
1:36 it slightly amusing that people writing
1:38 web code nearly always assume that it's
1:40 different and unique to writing any
1:42 other non-web code. It's not that I'm
1:44 picking on web developers here because
1:46 back-end developers call me out because
1:48 their code is special, too. So do
1:50 developers of embedded code, safety
1:53 critical code, package systems, banking
1:55 systems, games, and pretty much every
1:58 other type of code that people write.
2:00 We're all special cases, and so the
2:02 recommendations of how to do a better
2:05 job don't apply to us is seemingly what
2:06 they're saying. In the case of
2:07 test-driven development, there are
2:09 examples of organizations doing it
2:11 successfully for all of these different
2:14 types of systems and many, many more. So
2:17 maybe the special cases even if they are
2:19 sometimes different aren't so special
2:22 after all. Actually reading artim stuff
2:24 there is a lot that I can agree with.
2:26 Yes it is disappointed when pe people
2:29 take the wrong message. But describing
2:32 test-driven development as pedantic
2:34 constraints that no engineer can follow
2:37 is simply and obviously factually
2:40 incorrect because here I am a software
2:42 developer who can use these so-called
2:45 pedantic constraints to my advantage
2:47 even for that apparently tricky form of
2:51 uh coding writing web apps. To be fair,
2:53 I I do like have to give credit. I do
2:56 like a falsifiable statement and even
2:58 more so when I get to falsify it. I
3:00 suppose it depends on the nature of web
3:02 apps that we're talking about. If your
3:05 web app doesn't do very much and so it's
3:06 mostly about playing with the aesthetics
3:09 and usability, then yeah, TDD is not
3:11 lots of help for that part of the
3:13 problem. But my point is that I want to
3:16 test the other bits of the system. the
3:18 SIS the stuff that constructs the models
3:20 that I may later display retrieves and
3:22 stores the information interacts with
3:24 the other parts of the system. Then I
3:27 can keep those tests passing so that I
3:29 can more freely change the parts of the
3:31 code that that are focused on the pixel
3:34 painting and more freely experiment with
3:36 usability and nice look and feel.
3:39 Actually, I saw Artm's tweet because
3:41 Kent Beck responded to it on Twitter,
3:43 and I saw Kent's response first, which
3:46 naturally I'd recommend. Kent outlines
3:48 his take on the importance of small
3:52 steps with a simple example, showing how
3:54 we can make progress in unplanned
3:57 incremental ways. Even when we don't
3:59 have a grand master plan of the eventual
4:01 destination, I wanted to pick up on
4:04 essentially the same point of view as
4:05 Kent, but from a slightly different
4:07 angle. This represents a common
4:10 antiattern that seems to me to be at the
4:13 heart of RTM's post. That is imagining
4:14 the code that you will write when you
4:17 write the test. This is a very common
4:21 mistake for TDD beginners. Um they're so
4:22 used to diving directly into
4:25 implementation that they can't prevent
4:26 themselves from doing it even when
4:28 they're being asked to start somewhere
4:31 else. In this case, by writing a test.
4:33 So what they do is because they are
4:35 being nagged by someone like me, a
4:38 trainer to start with a test, they
4:40 imagine the code that they will write
4:42 and then they write the test to test
4:45 that imagined code. This has the same
4:47 failure mode as writing your test after
4:49 the code. Your test will be now be
4:52 coupled to your implementation. In this
4:53 case, an imaginary
4:55 implementation. What Kent is talking
4:58 about in his post is very different to
5:01 this. this and does not rely on any
5:03 perfect foresight at all. TDD is a
5:05 process of dividing the design of our
5:07 software into a series of small actually
5:10 tiny steps that incrementally evolve our
5:12 software towards more effective higher
5:14 quality solutions. This is the real
5:16 trick, the real skill of test
5:19 development to learn a more incremental
5:22 approach to design. So this is a million
5:24 miles away from clever developers
5:26 intuiting perfect solutions before they
5:29 start. I think that not only that this
5:30 is a misread of the intent of
5:32 test-driven development, but actually
5:34 misses what I think of is probably the
5:37 most important value that it brings.
5:39 Test-driven development creates a
5:41 pressure on us to work in these small
5:44 steps. In my experience, the more you
5:46 practice test-driven development, the
5:48 better you get at reactively steering
5:50 the design rather than attempting to
5:53 intuitit it from the start. In the long
5:55 run, this gives us a more effective,
5:57 more scalable, higher quality approach
5:59 to design overall, giving us clear
6:02 feedback on our design choices after
6:03 each small step. If you'd like to give
6:05 test room development a try, do check
6:08 out my free tutorial or maybe even the
6:10 paid for full more extensive TDD
6:12 training course. There's links to both
6:14 of those in the description below.
6:16 Test-driven development is a more stable
6:19 approach for lots of reasons. But one of
6:21 the ideas that may seem subtle but is
6:23 deeply important in terms of the
6:24 stability and effectiveness of this
6:26 approach is that it keeps our options
6:29 more open after each small step. Which
6:32 is why contrary to assertion and the
6:34 doubts of the people that say that TDD
6:36 isn't possible unless we have perfect
6:38 foresight, TDD works better than the
6:41 alternatives when exploring new ideas.
6:43 Let's imagine three ways of designing a
6:44 system. big upfront design which I'm
6:47 pretty sure RTM's not recommending an
6:50 exploratory approach as he describes it
6:52 and TDD or what I think of as
6:55 exploratory with tests in big upfront
6:57 design you think really hard and
6:59 depending on the scale of the problem
7:01 maybe you try to capture your design in
7:04 some intermediate form so that you can
7:05 remember all of the detail as you go
7:07 through the process of thinking really
7:09 hard and maybe you use these models then
7:11 to explore and evaluate your design
7:14 choices in the old days, I used to do an
7:16 awful lot of this kind of thing. I was
7:18 actually pretty good at it and built
7:20 lots of systems this way, but it's
7:22 pretty limiting overall. People working
7:24 this way weren't stupid or naive,
7:26 though. They tried to find ways to break
7:29 the problem down into smaller steps. And
7:31 we ended up with modeling languages like
7:33 UML and complex development process
7:36 frameworks like rational unified process
7:38 that were meant to help. But I think
7:40 that the problems with this approach are
7:43 pretty fundamental and broadly fall into
7:46 two categories. Leaky abstractions and
7:48 lack of real feedback. The abstraction
7:51 problem is evident. Our aim is to
7:54 capture a design. In reality, the design
7:57 of our code is written in code. So,
7:59 however else we represent our code,
8:01 formal or not, if we're doing that
8:04 without the actual code, there's going
8:06 to be an abstraction layer on top of it.
8:08 All abstractions are leaky and
8:11 diagramming techniques like UML are
8:13 leaky in several problematic
8:16 ways. Some ideas are much easier to
8:20 express in code and some in diagrams. So
8:22 there can be a huge mismatch in the
8:24 levels of abstraction between the
8:27 diagram and the code either way round.
8:29 So something that looks complex in a
8:32 diagram may actually be trivial in code.
8:35 Try writing a for loop in UML. and
8:37 something that is complex in the code
8:40 may look easy in a diagram. Both of
8:42 these are bad if we're trying to make
8:44 choices about our designs based on
8:47 pictures and in the absence of real
8:49 code. Don't get me wrong, there's
8:52 nothing inherently evil in diagrams. UML
8:55 is a fine tool for conveying some ideas
8:58 clearly, but I think it's most effective
8:59 when used at a very different level of
9:02 abstraction to code. And it's a big
9:04 mistake to confuse the two.
9:06 It's too easy to express an idea in a
9:08 diagram that is just plain stupid when
9:10 it comes to the code. The abstraction
9:13 leaks. The second problem with upfront
9:15 design is how do we get feedback on the
9:18 validity of our design. There was a big
9:21 push through the 1980s and '90s to try
9:24 and formalize this with more formal
9:26 diagramming techniques, defining rules
9:28 so that the tools could validate the
9:30 diagrams. This would be our feedback
9:33 that checked our designs. People were
9:35 talking a lot in those days about model
9:38 driven design assuming that we would
9:40 raise the level of abstraction for for
9:42 development by using diagrams and then
9:44 generating the actual code from the
9:48 pictures. Problem was this never really
9:50 worked out very well. It turns out that
9:52 code as text is remarkably good and
9:55 flexible at representing ideas. In this
9:57 case, a picture really isn't worth a
9:59 thousand words. Code is often more
10:02 precise and more concise. In general,
10:05 software demands an annoyingly pedantic
10:07 level of detail and precision, and it
10:08 turns out that text is better at that
10:11 than pictures. Pictures can help us
10:14 navigate though, but do a poor job of
10:16 specifying things in sufficient detail
10:18 to be reliably
10:21 executable. Code is often more concise
10:24 and more accurate representation of what
10:26 it is that's really going on. which
10:27 means that when we come to realize a
10:30 diagrambased model of a design, we'll
10:32 often find mismatches at the level of
10:35 the code. So our design assumptions can
10:37 easily be wrong until we get to the
10:39 actually implementation. The next level
10:42 up from big upfront design like this is
10:45 RTM's suggestion of a more exploratory
10:48 approach. He describes it as prototype,
10:51 iterate, test. Obviously, I think that
10:53 testing at the end like this is a big
10:56 mistake. it misses out on the large part
10:58 of the value of test-driven development
11:00 which is the impact that test-driven
11:01 development has on the design of our
11:04 code. I believe that TDD forces us to be
11:07 the first consumer of our code and so
11:08 gives us the first and strongest
11:11 feedback on the quality of our design
11:13 choices as a result. But as well as
11:16 missing out on that so valuable feedback
11:18 on our designs, leaving the testing
11:21 until you're otherwise finished also
11:23 misses out on the defensive value that
11:25 the tests give you in test room
11:28 development and and so making you more
11:30 able to iterate. How do you know that
11:33 your iterations are still working and
11:34 leading you in a sensible direction if
11:36 you don't have those tests? Practically,
11:39 I don't see a lot of difference between
11:41 RTM's recommended approach and the more
11:44 classical big upfront design because big
11:45 upfront design never knows how bad your
11:48 choices are until it's too late.
11:50 Prototypes are fine. They don't replace
11:52 test room development though. Its job is
11:55 something else entirely. I think that
11:58 RTM's mistake here is another common
12:00 though understandable one. Seeing test
12:02 room development as being primarily
12:04 focused on testing. After all, test is
12:06 right there in the name. And I agree
12:08 with him very strongly on the value of
12:11 tests and automated tests in particular.
12:13 But I still think that it is the
12:15 secondary value of test room development
12:18 compared to the impact of TDVD on the
12:20 development process overall and
12:22 specifically in terms of driving better
12:25 design choices and encouraging us to
12:28 work in smaller steps. So how does this
12:31 compare to TDD? Well, Kent Beck's point
12:34 is very important one. TDD encourages a
12:36 more incremental, more evolutionary
12:37 approach to design, development, and
12:40 testing. We proceed in small steps and
12:43 usually try to start with what seem like
12:46 the easiest steps. In my test room
12:48 development training courses, I often
12:50 use a coding exercise to add fractions
12:53 to teach this incremental approach. My
12:56 first test is usually to begin with a
12:58 test that adds the simplest possible
13:00 kind of fraction that I can imagine, a
13:02 whole number, something over one.
13:05 I don't need to have decided anything at
13:07 all about my solution at this point.
13:09 It's obvious that this is a valid
13:11 question that my code must one day be
13:13 able to answer. And I'm guessing that
13:15 this is probably the simplest kind of
13:17 addition for fractions that I will ever
13:18 need. So the implementation will be
13:21 simple. But even if my guess is wrong at
13:24 this point, there's no real cost and my
13:26 code and test will be really simple. So
13:29 I can change them easily playing with
13:31 the design freely. And once I've made my
13:34 test pass, I can still change the
13:36 implementation detail as much as I like
13:38 without changing the test. All in the
13:40 secure knowledge that I still need to
13:42 add integer fractions and then I haven't
13:46 broken anything yet. So I'm picking a
13:48 behavior that I'd like my code to
13:50 exhibit, but that won't be too
13:52 challenging and won't demand that I
13:54 write anything complicated to make it
13:57 pass. The subtlety that I think non-TDD
13:59 people often miss is that writing the
14:02 test is about designing the external
14:05 interface to your code. The refactoring
14:07 step is where we design the
14:09 implementation detail, not the red
14:11 testing step. This means that we can
14:14 often play with our design before we
14:17 have an an implementation. Even I can
14:19 and usually do play with the interface
14:22 of my code at this stage while it's what
14:24 is probably the simplest state that it
14:27 will ever be in a test calling a public
14:29 API with as yet no working
14:32 implementation beyond that it compiles
14:34 allowing me to quickly and easily play
14:36 with the public interface to my code
14:39 from the perspective of a user of it. I
14:42 can use this to play with my design for
14:45 essentially zero cost of change. This is
14:47 much easier than building some prototype
14:50 of the whole thing. Even in this very
14:52 first test, I can be making decisions
14:55 before I've decided how any of the
14:57 implementation needs to work. Before
14:59 I've even begun to think about the
15:02 details of the problem that I'm solving,
15:04 I'm certainly going to need a fraction.
15:06 And so I can start writing that. I can
15:09 answer questions like, how will my I
15:11 represent fractions in my code? How will
15:13 I create them? How will I compare them?
15:16 and so on. I've started designing with
15:18 the only overhead being two or three
15:21 lines of test code and enough
15:22 implementation free production code to
15:24 represent the public interface to the
15:26 code that I plan to write
15:29 later. I find this approach to
15:31 exploration so much more effective than
15:33 simply thinking hard and trying to solve
15:36 the problems in my head. Once I have one
15:38 test in place, I can guess at the next
15:41 small incremental step and write that.
15:43 If I don't like how my design looks as
15:45 it evolves, as I try to cope with
15:47 incrementally more complicated versions
15:49 of the problem, then I can go back a
15:52 step or two now better informed and
15:55 revisit my assumptions from earlier. All
15:57 still with tiny minute fragments of
16:00 code. No genius level foresight required
16:02 anywhere here. Mainly a slightly more
16:05 organized, slightly more disciplined,
16:06 slightly more incremental approach to
16:09 design. Ultimately, what this gives us
16:12 is the freedom to design much bigger and
16:15 more complex things because we're always
16:18 building on something that we know works
16:21 because we tried it. Relying on our
16:23 tests to supplement our memories and our
16:25 understanding of the code as the code
16:27 gets bigger and more complex than we can
16:30 easily hold in our heads. This is a much
16:32 more scalable approach. Thank you and bye-bye.
16:35 bye-bye. [Music]