"Path-focused design", of stories, architecture, code, is design that understands that we can only reach a distant City on the Hill by taking one stride-limited shipping step at a time.
Sunday muse-day, comfort food for the geek in me and in you, but please remember, we don’t really just want to change code, we want to change the world.
Stay safe, stay strong, stay angry, stay kind. Black Lives Matter.
In the software design theory of the ’80s and ’90s, the complete examples tend to share three features: small scope, single-thread construction, aimed at a fixed and final target.
- By small scope, I mean to point out that the size of these examples is, by modern standards, trivial. A fully worked-out case might have 5k lines of code, in OO terms, 20-30 classes, only slightly more rich than today’s standard pedagogical ToDo app.
- Single-thread construction means that the design is conceived and executed by a single individual, or possibly a tiny team, with no cross-coordination, no mixed multiple goals, no management or bureaucracy, and no synchronization issues.
- The fixed and final target is a desired product that is a "City On The Hill" (CotH), a perfect and complete artifact, built offline, now ready for occupancy, finished and final, with no further changes needed.
I joke that the development process for these folks was a complex three-part approach: "1) Nothing. 2) Everything. 3) Move on." But that’s just to make you smile, I have tremendous respect & affection for those works. They helped make me a stronger geek, a lot.
The issue is that, like nearly all strategists all the time, they were fighting the last war. None of these features, so true in the world of the ’70s and ’80s, holds true in the modern professional software circus. They’re just not there.
The majority of us work on apps of at least 10 times that scope, sometimes 100 times, with many other people, in multiple teams, possessing widely varying skillsets, in an environment of nearly constant change.
The missing element of the analysis, the one lurking in the hole where those features were once largely true and are now largely false is "temporality", the fact of time, and its rising importance in any viable development strategy.
The far larger scope means it takes far longer to get to the CotH.
To go faster, we use many more people, working in parallel, and having to address issues of synchronization. And the lifecycle of modern apps is far longer, requiring lots of concomitant post-release change.
In addition, the funding models in software are also a huge factor: in response to an awareness of time, it is usually imperative that we ship some viable weak version of our solution "yesterday", use it to gather money, then spend that money enhancing the weak first answer.
When someone sets a software problem, I instantly start working out the solution in my head. The truth is, sometimes before the second sentence of a problem is out, I’m already mentally sketching the design that could handle it. I can’t help myself.
"Baby, it’s a geek thing."
I imagine a finished solution, then decompose it into a bunch of finished parts. This is a great start, it’s the 20th century finish, but in the 21st century, it’s only a start, tho we don’t teach that terribly well. It’s only a start because of temporality, the time factor.
Ninety percent of the problems we actually face as geeks aren’t "solve this problem eventually, starting from a clean page", they’re "make our existing solution do this also (or instead) as soon as possible". That little tweak, from greenfield to brown, makes all the difference.
Path-focused design means that we have to design not just the finished parts, but the "shipping stride-limited steps" needed to get us there, and that will mean taking steps that involve shipping parts that sometimes don’t even resemble the finished parts.
"Shipping stride-limited steps" is a mouthful. It’s the placing of two constraints on our definition of a step. 1) It must be smaller than a certain size in elapsed time. 2) It must be independently shippable at the moment of completion.
By "independently shippable", we mean that its changes can be deployed right now. For a programmer coding, we’re talking a change that is guaranteed to not break what’s working. For a producter storifying, we’re talking a change that does not make the user’s life worse.
What size of elapsed time is suitable? For a programmer coding, we’re limiting size to a matter of something under an hour. For a producter storifying, we’re limiting size to a matter something under two days of team effort.
Now, as soon as programmers or producters hear constraints like these, they declare them to be impossible to meet. They’re not impossible to meet, they really aren’t. Rather, the standard faux-efficiency model we use in the trade is kicking in and making it seem that way.
What’s going on is that folks are secretly holding on to two other constraints, two that path-focused design explicitly does not hold. All four constraints aren’t always impossible, but at the very least, they’re usually very damned hard to meet.
Extra faux-efficiency constraint #1: Each step must create a "final part".
Another way to phrase this, no step is allowed to change anything that was changed in a previous step already. This is the finality constraint.
It is baked deeply into the trade, from its earliest days.
Extra faux-efficiency constraint #2: Each step must add user-detectable value.
Another way to phrase this, no step is allowed that changes things under the hood but does not also change the experience of the user for the better. Call it the better-for-user constraint.
Now, as I say, when we put "stride-limited", "independently shippable", "finality", and "better-for-user" together as constraints, it may or may not be possible to find a step. Sometimes we get lucky. But it’s much harder than meeting just the first two constraints.
And that’s why path-focused design is different from traditional design. It seeks to meet all four constraints, but when it can’t, it settles for three, and when it must, it settles for just two: "stride-limited" and "independently shippable".
I’ve written about this a lot, using different language. If you’re a producter look at my iterative user value article. If you’re a programmer, it’s woven throughout my coding advice: Iterative User Value | GeePawHill.org
A very simple example, as I say there are lots more spread through my work. We currently have service A sending an email, then passing an event to service B. But we want service B to send that email, cuz it knows new extra stuff to put in it that Service A doesn’t.
The one-step finished solution is easy. Move the part where the email is sent from Service A to Service B, make Service B send it with its cool new data. Sit back and wait for the stock options to mature.
But. Not so fast, Eddie.
The two services are owned by two different teams, with two different backlogs and their accompanying priorities. Both services are in the field right now in a 4 9’s situation. Sending those emails is a big deal. Getting this wrong would be Sev 1.
So let’s not do this in one step, though if we weren’t in different teams on different schedules in a running app with high uptime requirement, one-step would work out fine, because it would truly be one stride-limited shippable step.
Path-focused: Add a new boolean to the A->B event, saying "I sent the email." Make A send it, hardwired to true. Make B receive it. Make B act on it: if the flag is false, send the email. Make A hardwire it to false. Remove the flag and the if’s.
What’s the positive difference, the benefit?
There is zero synchronization. Each step is independently shippable. Each step is well under a day. The steps can be performed at any time as long as they’re in order. We’re still testable, running, and shippable after every step.
What’s the negative difference, the cost?
Someone had to think of it. We are changing the same code twice: We’re adding a flag to an event that we fully intend to remove, adding if’s on that flag knowing full-well that a later step will change what we already did.
This is path-focused design.
It says that "where we will wind up", as important as it is, must always be accompanied by "how will we do it in such a fashion that we can guarantee our steps are independently shippable and small enough to gain the intrinsic benefits of stepping?"
Now, I’ve been talking about "path focused design" as if it were a new idea. But of course, it’s not.
The most common word we use to describe this is "iterative". But we could be iterating, and still be taking large unshippable steps. Many teams do.
Focusing on the path to the City on the Hill is incredibly important for efficient software development. If we always and only pay attention to the CotH itself, we’ll create slow-moving bureaucracy-laden high-risk development processes, & that’s not a cost we can afford to bear.
(And one more tiny thing before we go: This looks like it’s about changing code or changing product, and it is. Because it’s about changing anything. Whatever the domain of our City on the Hill, we need stride-limited shipping steps to get there.)
Do you love the GeePaw Podcast?
If so, consider a monthly donation to help keep the content flowing. You can also subscribe for free to get weekly posts sent straight to your inbox. And to get more involved in the conversation, jump into the Camerata and start talking to other like-minded Change-Harvesters today.