The economic aspects of microtest TDD are inextricably tied to the operational aspects of it. If we concentrate only on the artifacts involved, we will lose the picture and misunderstand the value proposition.
As a professional geek, the heart of my job is changing rigidly structured imperative text in collaboration with other humans, in order to meet a variety of locally or temporally aligned goals.
That’s fancy talk for "I change code, in community, for money."
The central event is the changing of code. (It’s remarkable to me how very little attention we pay to this in the trade. Instead, we focus almost exclusively on getting to a state — usually imaginary — where no changing occurs.)
So, if changing code’s the thing, there are some factors, in the actual operation of changing the code, that seem to loom extra large when I talk about production or efficiency or economics.
Factor: To change code, I have to understand what it does.
The relationship is direct, topping out at the Heinlein-esque "grok" level, where I can change code with great rapidity. Bottoming out at "I have no clue", where every change takes a long time and a lot of courage.
Factor: My productivity is highly dependent on sizing.
Roughly put, the bigger the change, the more I have to hold in my head while I do it, and the closer I come to hitting the limit of my human mental bandwidth.
Factor: Highly structured imperative text is finicky and peculiar and inhuman.
So I do better when I can frequently and easily confirm that what I said is what the text-receiver heard is what I meant. The faster I get that confirmation, inch by inch, the faster I go.
Factor: Most of the changes I make that break things are actually rather simple mistakes.
Though we love rocket-science errors, they are a tiny minority of real change-breakage. Most bugs are — a technical term here — "dumb-assed". Off by one, inverted condition, and so on.
Factor: The next most frequent cause of my change-breaks is when I make regressions without knowing I have done so.
I see a lot of "toggle-bugs", where I fix A and break B, then I fix B and break A, and so on. Knowing that my code here didn’t break my code there is really useful.
Factor: I don’t work solo. Other people are changing the code even as I’m changing it.
If I have to worry that they suck and are breaking my work, or that I suck and am breaking their work, I go very much slower.
So with all these factors in mind, and likely some I forgot to mention, we can almost ease in to the economics of microtest TDD. Before we do, though, there is one more really important aspect of all this we need to both understand and accept: perfection is not on the table.
Perfection is not on the table: It is not possible for me to make changes to my highly structured imperative text with perfect certainty that they contain no errors. This is not an opinion, and it’s not because practice is shoddy, it’s a direct consequence of the halting problem.
So the question we’re approaching is not now or ever the question of "how can we prove our changes work?". We can’t prove that, under any circumstances. We are permanently thrown to ask a lesser question: "how can we become reasonably confident that our changes work?"
This is a really important distinction, because the number one source of misunderstandings about TDD, microtest or otherwise, is that it is a failure if it can’t prove that no bugs are created by a given change.
Okay, reeling all that back in. Let’s talk about how microtest TDD works economically. The short version: it’s the cheapest way I know to combine the factors above (and others) to mitigate the effect of my most frequently occurring mistaken changes.
Microtests increase the speed of my understanding of both my own code and other people’s. They form a kind of "grokking scaffold" around the shipping code. The tests are little usage snippets, they’re answers to common questions, and they’re executable documentation.
Microtests inherently size the changes I make. They are so small themselves, the changes they’re validating must also be small. This leans in to my very real very stark very severe limits on mental bandwidth.
Microtests catch illegal highly structured imperative text by running it in a very narrow exercise case. This is particularly useful when I’m working with dynamically typed languages. They only show such errors when they run, and microtests run the code w/o running the app.
Microtests are particularly good at catching that category I called dumb-assed mistakes. It’s because they’re so tightly focused around my branching logic, exercising it case by case in little chunks. It’s not that they make hard bugs easy, it’s that they make easy bugs easy.
Microtests persist in the source vault, they accumulate as the codebase accumulates. This means that toggle-bugs are caught the instant they happen. Nothing stops all possible regression, but microtests catch many of them at a very small expense.
Microtests capture not only my expectations/usage of my code, but others, of theirs, and we can use them to cross connect, e.g. my expectation but on your code. A broken microtest is a first-rate collaboration focus, something many of us need frequently.
So add all this up, and we get a sense of the economics of this microtest TDD game. It comes down to a few short statements.
- The value is only lightly vested in the artifact, and much more heavily tied to the way building & using the artifact affects us operationally.
- It does not provide its value by proving that there are no bugs, but by better fitting the cost to benefit for finding a subset of common bugs. It doesn’t make hard things easy, it makes easy things easy.
- Its value also comes from its use as a forcing function: it forces smaller changes and — we didn’t develop this idea herein — it dramatically influences designs in ways remarkably similar to modern software theory, e.g. SOLID principles.
- It provides its value in an unusually "human-centric" way, putting special emphasis not on the code so much as on the humans that change that code, working with their strengths and against their weaknesses.
There are so many other things we have to talk about with this idea. I think, next, we should take a simple case and see it in practice. (I won’t stop there, tho: we burn too much pedagogy on unrealistic problems in this trade.)
So stay tuned!
The GeePaw Podcast
If you love the GeePaw Podcast, show your support with a monthly donation to help keep the content flowing. Support GeePaw Here. You can also show your support by sending in voice messages to be included in the podcasts. These can be questions, comments, etc. Submit Voice Message Here.