When we did our compare & contrast of the working models underpinned by Change-Harvesting theory (CHT) vs Rework Avoidance Theory (RAT), we temporarily sidestepped the specific differences of the implementation part.
Let’s drill in on that today. Read the first post here:
It was quite a sidestep: other than the implementation part of the two models, they have much in common, with the key difference being the highly iterative nature of CHT’s approach.
If we squint a little, & we don’t talk implementation, we can see the CHT model as being a daisy-chain of RATs. Each step is a "mini-RAT", on a scale of a couple of team-days each.
But that’s only if we ignore implementation differences. And they’re too big to ignore.
In fact, I’ll venture out on a limb a little: ignoring the tremendous difference between RAT implementations & CHT implementations amounts to the second-biggest reason most "agile transformations" fail to bring actual improvements.
So what’s that difference between RAT implementation and CHT implementaion? In one word: changeability. CHT implementations use a variety of techniques to ensure that the codebase is optimized for the act of being changed.
Remember the squint? "CHT is like a daisy-chain of mini-RATs if you ignore the implementation difference." We’re squinting when we say that, because it isn’t a chain of mini-RATs, it’s a circle of them.
Remember that RAT implementations are final. Their input is a blank page, and their output is the final component, ready to be assembled in the big bang just before we ship. But CHT steps are connected in a loop. The output of one step is the input to the next one.
At each pass through the CHT cycle, we start from an existing codebase and we change it. The RAT assumes finality. The CHT assumes iteration. And that is why the single word for that difference is "changeability".
Fine, what’s changeability, anyway, & how does CHT implementation optimize for it? There are three key attributes: Optimizing for change is optimizing for 1) control, 2) monitoring, and 3) expression. We’ll take each in turn and see how it affects our implementation technique.
First, control. Simply put, I can’t change code I don’t have control over. Modern software involves lots and lots of components, arranged in a large directed graph of dependency. Some nodes in that graph are "ours", and some are not. We can’t change the ones that aren’t.
Third party libraries & frameworks are wonderful. They give us much greater capability than if we relied only on ourselves for our output. But no free lunch: we don’t control their development. We can’t change them. We can’t fix their bugs, we can’t decide when they release.
Control manifests in CHT implementation as "boundary management". We make conscious decisions about how to connect "our" code to "their" code.
In the ideal, we know every such connection-point, and our dependency graph looks like a big core blob of our code in the middle, with their code above and below us, and a thin wall of our boundary classes, isolating what’s changeable from what’s not.
Of course, there are libraries and their are libraries. It’s possible to write a modern app without using the "their" code of a string library, for instance, and isolating ourselves from it would be foolish waste. But the point is, in CHT we make that decision very consciously.
The newer, less stable, less standard, less supported, any given framework or library is, the more carefully we seek isolation. And when we can’t decide or don’t know? We default to rolling code that isolates theirs from ours.
There are techniques for doing this, and choosing and applying them is a distinct programming skill. In some previous muses we’ve talked about some of them, and I’m sure we’ll re-visit them time and again, likely even discovering new ones as we get better at all this.
Second, monitoring. To quickly change a working thing, you must know how it is working now. Remember the offline/online distinction? In the CHT we’re changing a working program. It’s imperative that we keep it working, and monitoring is how we do that.
The other word for monitoring is "testing", of course. The main impact of monitoring as a concept in CHT development is our extensive focus on testing. We generally use TDD, which gives us monitoring, but also lends very strong support to both control & expression.
Remember, the CHT’s applies many changes over a period of time to a working system. Each change must leave the overall system in a "same-or-better" state. Determining sameness is largely tedious and mindless work, so we give that job as much as possible to the computer.
We don’t just test the code, we automate the testing of that code. When we discover bugs, we see them as holes in our test suite, and we add new tests every time, preferably before we even fix the problem. We even go so far as to prevent whole bug families in future changes.
Testing in this way can be done "after", but over time, we’ve determined that "before" works faster, cheaper, and better. We call the implementation techniques we use to do that test-driven development (TDD).
TDD is just like the isolation techniques we discussed in control: there’s a lot to it. It takes practice, it changes designs, and we apply it or don’t using our human judgment.
Third, expression. You can’t change what you can’t figure out, so making things as easy as possible to figure out is the sine qua non of CHT’s programming model. It simply is not enough that a change leaves us in a working state. It also has to leave us ready for more change.
The emphasis on expression is one of the older aspects of the trade, long pre-dating the modern CHT model. The most famous paper in the post-1950 trade is Djikstra’s "Goto Considered Harmful" in the ACM, from 1968. It’s largely about how the "GOTO" statement hampers expression.
The staggering complexity of modern software is far beyond the reach of any casual approach to making our code express its operation. It takes serious and concerted effort to make code tell us what it means. The upside: the effort pays us back almost immediately in changeability.
In CHT implementation, we refactor our code, which means changing its expression without changing its monitored function. We do this on a permanent ongoing basis, before, during, and after the other changes that we make.
The RAT regards this behavior as waste: since every component is "final", all that matters is that it works. But the CHT doesn’t see any code as final until the day it’s de-commissioned. Refactoring isn’t ancillary or optional, it’s a key to making code that can change quickly.
Once again, expression isn’t one thing, or one style, or a context-free set of rules. It’s a variety of techniques, it takes practice, it changes designs, and it requires a great deal of judgment.
Wow. I told you this thing was too big to go into the other day. Every one of those three attributes of changeability: control, monitoring, and expression, is itself worthy of many of these muses. Pretty daunting. 🙂
Tho parts of the CHT model and the RAT models look the same from a distance, implementation in the CHT is dramatically different from in the RAT. It hammers on changeability, using 1) control, 2) monitoring, and 3) expression to optimize software development.
Supporting The PawCast
If you love the GeePaw Podcast, consider a monthly donation to help keep the content flowing. Support GeePaw Here.