TDD Pro-Tip: Build cells — logic nucleus on the inside, environment inputs & outputs on the outside, and a semi-permeable cell wall between the two.
The metaphor i’m proposing here is really about the nature of borders in code. Prior to TDD, my grasp of this topic was driven largely by the great theory of object-orientation that began to emerge back in the ’80s and came to dominate the modern s/w landscape.
As with so many aspects of the programming world, TDD as it has developed is shaping my understanding of this vital topic — the nature of borders — even as I practice it.
Borders can be approached at first as impermeable walls around some precious thing. (that approach won’t stand up, but go with me for a minute.) what, then, in the case of code, is the precious thing the borders are enclosing? In the modern synthesis, the precious thing inside the border is "change-enablement".
The border exists to separate areas where I change things easily from areas where I can’t.
This would be a shocking statement to nearly all of the OO folks in the ’80s, and to be honest, it’d prolly cause at least a great deal of nervousness and uncertainty if I said it to most programming theorists today.
Better spend a minute on that, then.
Programming computers for a living is changing code. This notion lies near the center of the movement we’re all in, it’s even codified in one of our founding texts, Extreme Programming Explained: Embrace Change. The idea’s influence reaches every corner of the modern synthesis.
But the movement didn’t invent that idea from whole cloth. Many of those ’80s ideas about OO that are still considered valid were already aimed there, they just weren’t stated that way.
A lot of those are embedded in SOLID, which is the most widespread of the modern statements about how to do programming, so i’ll use those terms as my illustration of this.
Start with encapsulation, what most of SOLID is really talking about. The original view of encapsulation was that we do it to hide implementation details. Why would we want to do such a thing?
Simple: so we can change them when we want.
The modern statement of the LSP says derived classes must be entirely substitutable for their super classes w/o any clients knowing the difference. Why would we care about such a thing?
Simple: so we can change part of our code’s behavior w/o changing all of it.
The ISP says client code should not have to depend on server code it does not need. Eh? Why would we care about that?
Simple: anything we don’t depend on is able to be changed without affecting us.
The DIP says we want to depend on abstractions, not concretions. Why not?
You know the drill: cuz concretions change more often than abstractions, and we want to enable those changes to happen without affecting us.
I’m not gonna keep going, you get the drift. Returning to the main theme, then: we make borders a certain way because it makes it easier for us to change code on one side or the other, and our whole entire livelihood depends on changing code.
Enter TDD. It doesn’t disagree with SOLID principles (or their founding base), it actually thickens and extends them, tho it also changes their flavor a little, taking one from the more theoretical to the more hardheaded pragmatism of a working stiff.
If the borders drawn from SOLID are about enabling change, those same borders drawn from TDD are even more focused on change-enablement.
TDD morphs "change-enablement" a little, you see. Because TDD says we have maximum change-enablement when we have the same attributes we loosely call SOLID coupled surrounded on all sides by perfect testability.
(Aside: "perfect" in the prior sentence is a very large topic, and one that’s dear to all persons pursuing TDD, but this thing is already too damned long, and we’ll defer describing "perfect" for another day.)
All of this means that the nucleus in these cells I propose is a nucleus of tested branching logic. That is the precious thing on the inside.
What about the outside? What’s outside that nucleus of tested branching logic? Well. We can divide that into three parts. 1) inputs and outputs for the nucleus. 2) other cells. 3) everything else.
(For my poor victims who were following along in the far longer thread I just killed. Hang tough. We are going to stop right here right now, and continue the other parts of this in other muses.)
The inside nucleus is tested branching logic. The outside environment is a) the inputs & outputs to that logic and b) everything else. The cell-wall boundary is what lets outsides of type a in and keeps outsides of type b out.
We’re not nearly done.
Have a strange evening!