Part of 9 in the series
The judgment premise is one of five underplayed tdd premises.
The judgment premise is simple to word and vast in its extent. It says, "tdd relies absolutely on individual humans using their human judgment." you might ask yourself, "what doesn’t rely on human judgment?" but there are lots and lots of activities that are entirely mechanical, judgment-less, and geekery is full of them. We work with judgment-less systems every day. We call them "computers".there’s merit to "algorithmizing", that is, making computable sequences that can replace ones that require human intervention, much merit. But sometimes our zeal for this activity gets the better of us.
The modern synthesis in coding, what I normally call TDD, but meaning TDD in the broadest sense, has at its core four activities: writing tests, making them pass, change-optimizing the code, and pushing the result to head. (i shorthand them to red, green, gold, push, and I trust you’ll know what I mean going forward.) in its simplest expression, we can see these as a simple cyclical sequence: red, then green, then gold, then push, then start over. We can elaborate on this — i’ve done so and many others have, too — to create a fancy flow-chart thing from it.
The judgment premise says that every one of the bubbles on this psuedo-algorithm is fraught with the requirement that humans use non-computable means to control it — to refine it, to transition around it, to actually do TDD.
The originators knew this, of course, and the serious practitioners know it, too. That’s why I call this a premise: it is baked-in all the way through. It’s an underplayed premise, in my view. We don’t make a big enough deal about it. That’s because when you’re inside TDD, your confidence in the premises is complete. They’re the air we breathe, and like that air, largely invisible but essential to our success.
The judgment premise, like the rest, is an effort to make this air visible to us. Let’s look at those four core activities, with an eye towards spotting the non-computable human judgments they involve.
Getting to red involves numerous judgments. First, of course, there’s the simple question of whether or not the code i’m about to put in calls for a test at all.i don’t test a function called ‘getX()’ that returns a field called ‘x’s value. I don’t know of anyone who does. My judgment is that such a test wouldn’t pay for itself.
Note: that’s not to say I never fuck up the implementation of ‘getX()’. It’s to say that when I do that, occasionally, I discover it so easily, usually the very first run, that writing a test for it would be a waste of time. (The money premise rears its mammonist head.)
The "no test for getX()" judgment isn’t a no-brainer, cuz no-brainers aren’t judgments, but it’s a tiny-brainer. As we proceed at getting to red, tho, some of these similar "no test for" decisions become quite a bit more brainy.and it isn’t just whether or not to test it’s very often what to test. Recently, I was laying out some UI in javafx, whose layout strategy is fuzzy AND buggy. I did most of my basic testing using a skeletal disconnected "ui-only" app with a GAK (geek at keyboard) approach.
I came to understand that if I wanted perfection, I needed to do the layout math "manually". That is, give me the width & height of client area, and let me compute and assert the layout from that. And that computation, I tested with a bunch of automated cases. There are still no tests for the actual rendering: my judgment has been validated visually.
A third judgment in getting to red: is this the right time to write that test? An extremely common move in the TDD game: sketch out a test and defer it. Sometimes I write the test and mark it ignored, sometimes I jot a note, sometimes I count on my leaky head to remember.
There are others i’m not thinking of just now, I feel sure. But let’s move on to green. Once I have a failed test I like, I get it to pass. Here, the use of judgment seems so gigantic and obvious it hardly needs enumeration.
Which methods change? Which objects get made? How do they interact? Do I need more data, less? Do I replace/extend this existing method, or do I need a whole new thing? All of these are judgment calls. They’re based on heuristics, on intuition, on sketches, on conversations, on habits and even mood. They are not computable.
When that test and all the other tests are green, we then hone in on the design of the code. Refactoring — getting to gold — is the business of optimizing our code for changeability. As with getting to green, I feel sure I don’t need to point out the level of delicate decision-making that goes on during the gold step. The fact is, change-optimizing code engages my judgment more fully than any other work I do.
That maximized-judgment thing, btw, is why your old master geek coaches seem to enjoy refactoring more than any other part of the work. It feels so good to use all of oneself in a single activity, logic, experience, intuition, anticipation. If companies were bright enough to pay us just to refactor code, I and many of my geek-coaching confreres would do nothing but that. It feels so much like the maximum expression of our long lives in geekery.
Pushing code, the fourth big blob on our psuedo-flowchart, is sometimes elided from the TDD skeleton, but it’s tremendously important. Much of the benefit of TDD in teams is captured because of continuous integration.
Sometimes after green I push, sometimes I don’t. I decide on the spot, a judgment. Further, sometimes i’m midstream in getting to red or green or gold, and I "anti-push". I revert. That’s a decision I usually make through some abstruse mixture of elapsed time & anxiety. (aside: we need to teach more folks about the huge value of restarting. I revert work-in-progress all the time, instead of always and only laboring on. I learned this only because I was practicing CI in very small cycles.) the judgment premise is about the humanness of the software development enterprise, even that part of it, TDD, that is closest to the metal.
I’m not hating here on the many psuedo-algorithms we make. As I say, i’ve done it myself. I understand the impulse, and they can be very helpful (briefly) to folks at the beginning of the climb. But psuedo-flowcharts are training wheels. Riding a bike with training wheels, however important as a short phase, isn’t being a cyclist. If you’re going to engage with the modern coding synthesis, with TDD, refactoring, CI, CD, and so on, you’re going to be constantly using the non-mechanical parts of you.
The judgment premise: in TDD we are absolutely, continuously, ineluctably, and happily entirely reliant on individual humans using their best individual judgment.