TDD Pro-Tip: TDD-blockers are everywhere for me, even now, after twenty years of practicing it, so I still try new ways to work around them, and I don't always find them, and I wait for next time.
A respondent had a great question, and this is a kind of "sideways" answer. The question: Given that we're halfway through an app w/o an architecture that TDD's well, should we start TDD now or wait for the next greenfield chance to do it right?
Let me start by telling you my current status. I'm on a small team with a modest app that's nearly done. This is a TDD team, and a very hardcore and experienced one. We're not in any way noobs, and we're true believers that TDD makes us go faster.
Different parts of the app have different levels of TDD-nature. Parts of it are exquisitely microtested. Parts of it are clunkily so. Parts of it are not at all tested, except by ourselves and our victims in live runs.
So. What gives, eh? Bunch of hardcore TDD'ers, a greenfield app, nearly a year of work, why on earth isn't this thing perfectly and uniformly TDD'd?
I'd say it comes down to two issues: a combination of ordinary human frailty and anti-testable component design. Let's look at those, then look at what we try to do about it, and finally look at the cost of where our efforts have failed, and why we keep trying.
The human frailty comes down to this: I am a successful working professional software developer. I was that for twenty years *before* I started TDD. If you put those two facts together, and if you've been following these streams at all, you know three things about me.
1) I am obsessed w/delivering a steady stream of user-value throughout development. The stuff I put out about sociotechnicality hits right here: you don't get to be any good at pro software development w/o being good at making AND showing AND capitalizing progress as you go.
2) I know a lot of ways to write code. They include TDD, and TDD is always my first approach because it makes me go faster. But they also include all the variant techniques I used in the twenty years before I learned TDD.
3) I don't know the right answer when I start. The only way to know the right answer is to have already solved the problem, and they don't pay me to solve problems they've already solved. I am constantly improvising, learning, guessing. I am a specialist at making shit up.
All of that adds up to the ordinary human frailty. I sometimes ship code too soon. I sometimes can't figure out how to use TDD effectively. I sometimes have to take several swings before I hit the ball. I sometimes strike out. I sometimes forge on anyway.
The second issue: there are often critical components of apps that are not made to ever support a TDD discipline. When an app we're writing relies centrally on them, finding a TDD way to use them is often a problem that's bigger than the app itself.
There's really no such thing as "greenfield" in pro software development, outside of microcode for new CPU's. Every app we write, in JS, in Java, in Ruby, in FP languages, every whole app depends on hundreds or even *thousands* of modules of code that were written before the app.
Fortunately, much of that code is TDD-indifferent. That is, it's usable by us whether or not we're TDD'ing. The string library in your favorite environment is an example of that.
Some of it is not. If you follow me for TDD reasons, you know that a key premise I accept is "steerability". It is that tests & testabilitiy should be first-class participants in design. If they're not, it's all to easy to build subsystems that aren't TDD-able.
An example is JavaFx -- or frankly any native UI framework I've ever used in my life -- is extraordinarily difficult to microtest. It doesn't expose the right things in the right place. It's inherently multi-threaded. It has necessary but horrific hacks for cross-platform needs.
So that's *why* these hardcore TDD'ers don't have everything in their shipping app as fully and richly microtested as everything else. It's just ordinary human frailty and the usage of central TDD-antagonistic components.
What do about this? We don't go gentle into that good night. We use a lot of the techniques our agility gives us to aim at variant approaches that simultaneously address our frailty and enable TDD near the un-TDD-able.
The list of things we do about this is really the list of practices and attitudes I call the modern synthesis. It's our whole approach, including CI/CD, TDD itself, our tools, our pairing, our retros, our safety, our fondness, our inside-the-team value-definer, and so on.
Interestingly, the *sameness* in our thinking helps us not give up. The *differences* in our thinking helps us come up with ideas we haven't tried.
It's really rather cool, and it's why I fell for this movement in the first place, and why I still advocate it.
In just one tweet: what's it costing us, those parts we haven't been able to TDD effectively? It's costing us so much time and energy and spirit. WE HATE IT SO MUCH. Without TDD, we *creep*. Every fix is a break. Every attempt is running the whole app. Every fail is unreplicable.
I started by saying this is a sideways answer to the question: "Given we're halfway through our non-TDD'd app, should we start adopting TDD now or wait for the next greenfield?" The oblique story I'm telling tells you the answer: start right now.
:) I guess it's not as sideways as I thought, yeah?
If you start TDD today, midstream in a non-TDD development effort, you will be repeatedly hampered by normal human frailty and TDD-antagonistic components, including many that you wrote yourself.
If you start TDD "next time", you will be repeatedly hampered by normal human frailty and TDD-antagonistic components, including many that you wrote yourself.
TDD doesn't "drop in" and it doesn't "overlay" on non-TDD development. Over time, it changes everything. Both of those clauses are important: "Over time", and "it changes everything".
TDD, and really I mean by that *all* of the modern synthesis, isn't a place you get to. It's a -- I am not a zen monk -- it's a way of being. A style of collaboration, a belt of tools, a way to *see*.
(It's a Weltanschauung. a word I use in preference to "worldview" precisely to jar you into looking up the idea, and specifically that part of the idea that has to do with conscious agency and it's lack.)
All the words we use to try to describe it just plain don't.
They hint, they flavor, the sometimes open people up, all good things. But the only way you're going to learn what it's like is to slowly grow your way into it by doing it as often as you can.
A lot of the bumps you'll run into are ones others have tackled. So be sure to ask out loud when you see them. Ask me, or ask my many coaching friends, or ask the internet. Some of the bumps, the only answer is to go around them. Some of the bumps we don't have an answer yet.
But I say go for it. It's great fun. I've been doing it for twenty years, and I'm happy to do it for another twenty.
Thanks, again, for the great question. It gave me a lot of inspiration, and I hope I shared some of it back.
• • •
Missing some Tweet in this thread? You can try to
force a refresh
In today's episode of Stupid Geek Tricks, I just basically invented Smalltalk using Kotlin.
Before you get angry, please know that I don't approve of this. I don't approve of a lotta shit I do.
120 lines of code. A marker interface for messages, a one-API interface for objects. Any class can handle any message it chooses to. Didn't bother with doesNotUnderstand, but it'd be an easy add.
Conceptually, it's straightforward: the Interactor wraps a Thing to give it a jump table that switches on the message subclass. It calls the Thing's register() to fill out that jump table. Any given Thing class can register any given Message+Handler pair.
Anyway, all and sundry, "geepawhill" is not a common moniker. Find me that way. I'm on mastodon, but I also have a whole website, geepawhill.org.
Backstory: "geepaw" means "grandfather", and now, to look at me, it seems obvious. Of *course* this bitter old fucker is a grandfather, just look at him. But "GeePaw" is actually a name I've had for over 30 years.
See, my wife is a little older than me, and when we first got to the bouncy-bouncy, her kids were already almost grown. I was present in the hospital room when my grandson was born. (It was gross.) And I became a grandfather at the ripe old age of 31.
Please, I'm sorry, please, remember through all this Elon-is-evil-and-stupid shit, remember, please, I'm sorry, please.
This ass-clown *bought* this place where you made community, he didn't steal it. And he *bought* it from the people who sold it to him.
Baby, you were so sure you were the customer, all along, and so mad to discover you were product, all along.
*Fucking* mastodon. There's servers. There's CW's, and bitchy people on your server telling you to CW your random rage-tweets. There's no funded algo stuffing your timeline, just your server's locals and your follows and their follows.
I once did a bake-off. It was in the early days with spotify, and spotify is the king-hell site for bake-offs. Type in "nessun dorma" and get 500 takes.
So I listened to maybe 200 or so, and I put together a CD of about 20 of them.
And one night -- yes there were substances involved -- I played it for my wife, and we listened to all 20 takes, and we chose our top 3. No commentary. We just listened, and chose our favorites.
Late at night, when no one's around, or they're all abed, or I'm drunk and I don't care, I sing this to the trees outside my house.
My range is very narrow, and it straddles right there, alto and tenor, and I'm old, a practioner of many vices, across many decades. But I sing it, and it fits in my range, and singing it makes me feel good.