23 Comments
Mar 29Liked by Kent Beck

This reminds me of your comment at DDD Europe - paraphrasing from memory: it turned out that 70% of software cost was in the maintenance phase; why not make it 99%.

The original poster implies that the system is “done” after his first set of iterations. That’s really unlikely.

One advantage of TDD (/BDD/etc) is to assume that you’re never done and that behavioural composition goes on for the lifetime of the system: many years or decades.

And that value should be deliverable early: the skateboard not the car.

Start with tiny behaviour, compose, iterate (over those many years or decades).

Expand full comment
Mar 29Liked by Kent Beck

Hi Kent,

I started TDD deliberately just a year ago. And it provides me all the benefits you have described here and there. But I think what is often underestimated is TDD as a tool for learning and rapid skill improvement. I think my Software Design skill never increased so steep as in the last year.

This view came up when listening to a conversation between Dave Thomas and Dave Farley. Where Dave Thomas described that he dropped TDD for 6 or 12 months and there was now bug increase or design decrease. So I just started wondering what this means. I think it’s definitely a learning practice, while making meaningful progress on the problem you are paid for.

Pairing is not always feasible, AI not always accurate enough. TDD is a very high available high quality feedback loop that supports learning.

Expand full comment
founding

It's much easier to tackle a programming task by breaking it down into smaller, actionable steps. Being able to test these smaller steps increases confidence in trying new approaches and algorithms.

Expand full comment

I struggled with recognizing this for years. I like to switch back and forth between big picture thinking and detail thinking. Everyone I found that talked about TDD for my first few years of exploring it acted like you only did little picture thinking, and then magically the big thing appeared without any intervention from yourself. I thought I was missing something important - the first place I saw someone acknowledge that you were responsible for how these little things went together to make the big thing was in the book "Growing Object Oriented Software, Guided By Tests".

But I love this post, I think it is something that those that know what they are doing take for granted, and forget to teach. I should be shouted from the rooftops, it should be one of the principles taught first.

Expand full comment
founding

Breaking big problems into smaller parts is a key skill in software development. It lets us work on parts separately or in steps, and helps avoid many issues caused by coupling. A lot of developers have trouble making software that is decoupled because they don't know how to identify and separate these parts to work on them one by one and then put them all together. This also makes testing easier, as it allows for testing small parts one at a time, something many people find hard to do with big, complex problems.

Expand full comment
Mar 29·edited Mar 29Liked by Kent Beck

> “If I wanted to be supported by tests as I code, how could I have designed & coded this differently?” & I’ve often found a way I could have applied TDD if only I had known.

I think this fundamentally gets down to going from "known to unknowns" (as you once taught me, it's pretty much how I always attack problems these days when I'm stuck.)

Often times the "known" thing is the next behavior change you want to make, especially if you already have testable code. In that case, the thing you can do to get immediate insight is actually writing the test, because writing the test is cheap and exactly describes the "what", no new insight is needed here.

Sometimes that's not the case. There's enough uncertainty about what you're trying to build. A good example of this is when you're trying to figure out a function signature: you may not want to just start by writing a test case for the new function signature, rather you'll call the stub function from another function as the next piece of code you'll write, because that's the information you need: you want to understand "what" the function should be.

Another example is that if you want to get insight into if something is technically possible at all, you would jump straight into the code and put assertions or exceptions or prototype to understand current behavior. Only once you validate feasibility would start writing tests. (would you throw away the prototype code you wrote before writing the test? Unclear, maybe context dependent.)

I think this can be extended to larger cases. If you're dealing with code that's not designed for testability, then refactoring for testability is the unknown. Perhaps you have to work with the code for a bit before it becomes clear how you need to make the code testable.

Expand full comment

For me, TDD is a technical expression of "Explain it to me like I'm five." It's amazing how many experts I've worked with over the decades can't simplify without time to "go think about it for a while." The few edges and corners of difficult to test are always held up as amusing examples of why not TDD at all.

Expand full comment

It seems to me that programming without tests is like climbing without a rope; as long as the climb is trivial for your level, it can seem fine.

Expand full comment

Hi Kent, great points. It's always a great experience learning with your vision.

In my experience, some people describe TDD only as the "Test First" approach. But it's not; it's a group of techniques and ways of thinking that aim to help you make better decisions while programming.

In the "TDD by example", you mentioned:

TDD is an awareness of the gap between decision and feedback during programming and techniques to control that gap. "What if I do a paper design for a week, then test-drive the code? Is that TDD?" Sure, it's TDD. You were aware of the gap between decision and feedback and you controlled the gap deliberately.

Expand full comment

My read of Artem's post, and my diagnosis of what's going wrong for him, is a little different. I see his response to TDD as symptomatic of systems where programmers:

- write a lot of glue code

- or are doing UI-heavy work

- or aren't experts in separating I/O from logic, or interfaces from engines

When my job is just gluing pre-existing pieces of unfamiliar software together, I can't effectively write tests for my glue up front. Since I don't know how the other pieces are actually going to behave, I can't write correct mocks for them. Sometimes I write system-level tests instead, but when the glue code is simple, this feels low-value.

UI work is similar, with the added wrinkle that I often can't know exactly what I want the UI to be until I play with it. Bob Martin said somewhere[1] that he often forgoes TDD for UIs and prefers to "fiddle them into place." I agree.

Of course, these problems can be mitigated by pulling out as much logic as possible into code that's disentangled from I/O. The current culture in React JS (which from skimming Artem's blog I infer he's a user of) is to lump state, I/O, and UI together and basically just make a mess. So I think that may be part of the issue here.

[1]: https://blog.cleancoder.com/uncle-bob/2013/03/06/ThePragmaticsOfTDD.html

Expand full comment

Well TDD and all test driven frameworks are broken by definition (including proto-iterate-test method).

It is a little #troll because to accept TDD we have to accept a “sufficient solution”. Test is an experimental approach and since the 19th century (from what I remember) we know that a test can infirm but not confirm. That’s why using a test approach we cannot have a general solution… we only have piece of code that works in certain circumstances.

As I say, it is a friendly troll, and TDD is a method that help the resolution of a problem by “saving” all steps that lead to a sufficient solution. And from my point of view, it is enough.

On the other hand, the proto-iterate-test thingy only save what the developer think at the very end with all the issue our brain have to remember the complexity of our work.

So… #tddForza :P

Expand full comment

I’m also more of prototype iterate test person but I still appreciate you writing this, Kent

After I read yours and Artem’s, I find both of you more in agreement than not.

> & I’ve often found a way I could have applied TDD if only I had known

I’ll emphasise the “if only I had known”

Because that’s the crux of the issue. With hindsight almost everything can be TDD.

This is the most important part that you and Artem are in agreement with.

If I’m right abt the crux of the issue, I’m less sure behavioral composition really solves that crux

Behavioral composition actually helps me o come up with a better guess at how parts of a larger thing work together.

IN that respect, it can be equally applied to TDD or prototype iterate test PIT henceforth

But it’s never a guarantee that the best guess will turn out right

Expand full comment

I think the biggest issue that almost nobody talks about here is scope. TDD was preached to me (and many others) as writing tests for every single line of code you have. This is literally insane.

In my experience, this is why people discount TDD. It's too many tests for unsure APIs that will simply serve as barriers while you are still exploring, trying to get to a good solution.

The "unit" in unit test became a class. And because of how people interpret the single responsibility principle, classes tend to become methods. Add all of this up and writing tests first feels like a complete waste of time. After doing this for a while, people associate TDD with this mess, eventually abandoning it altogether.

I don't practice TDD as much anymore because of these exact reasons. These issues happened in multiple code bases through decades in my career. TDD is still a great theory, but every implementation I've seen in the past 10+ years was so corrupt that I now mostly avoid it. Misguided evangelists and incentives to not think, often turn this great idea into a burden instead of a power tool.

Expand full comment

I agree that this behavioral composition skill exists. I feel like I can apply it to UI work, but only once I have had experience with the problems of a similar type and mastered the components of the system.

How would you recommend improving this skill without having to fiddle around for several projects and spend many years? Are there books, courses, or exercises that specifically target this particular skill?

Expand full comment