Week of 2022-03-14
The platform two-step
When we last saw Uncle Steve on his ladder, I left him a set of instructions about a less-risky way to adjust the ladder. Interestingly, these instructions roughly translate into a fairly useful technique for platform developers. I call it the platform two-step after a dance move, because platform development is a lot like dancing: both a skill and an art, and only fun when you’re into it.
This technique is something I find employed quite often by teams that make developer surfaces. Like the dance move, it is fairly intuitive and goes like this: if you desire to change an existing API, you need to first introduce a new API so that two are shipping in parallel (one foot steps out), then remove the old API (the other foot joins the first one). Simplifying it even more: to change, add one, then remove one.
This feels fairly straightforward, and appears to work nicely around the hazards of moving-the-ladder scenario we encountered with Steve. At the core of the technique is the realization that once shipped, APIs are basically immutable. Should we recognize that our first take on the API misses the mark, the only choice we have is to ship an improved API first, and then work to remove the first iteration.
In my experience, having patience between the two steps is where most platform teams struggle. While it may take time to ship a feature, the time to remove it is usually longer – and sometimes much longer. This was a hard-learned lesson for me. In early 2011, a few of us championed a set of specs we called Web Components, shipping them in 2014. We didn’t get it right and it took the Chrome Web Platform team until early 2020 to finally remove that iteration of the API. Some removals take even longer. The Web SQL Database is finally being removed this year, having enjoyed the “remaining foot to join” status for over a decade.
While bearing this patience, a rather unhealthy dynamic tends to develop. Because the cost of removing APIs is so high, we can’t afford to ship APIs that miss the mark. Especially when already playing a catch-up game, every new feature of the layer that doesn’t inch the vector of intention toward the user value is considered an opportunity cost. A typical symptom here is the platform developer edition of analysis paralysis, where new APIs are scrutinized extensively while under increasing pressure to “ship something, darn it!” APIs end up being over-designed and late because of the fear of missing the mark, translating into more fodder for deprecation, thus resulting in more resources being sapped from the API design.
I have observed teams stuck in that vicious cycle and I have been in them myself. The seemingly glacial pace of can be quite frustrating and appear as if the whole organization is about to seize like a giant rusted machine. The toil leaves little room for long-term thinking, with everyone just barely putting one foot in front of the other. It’s less of a dance, and more of a cruel march.
My intuition is that the way out of this situation lies through learning how to deprecate effectively. To complete the two-step, I need to learn how to migrate developers from one API to another. Put differently, in the platform two-step, the most important practice hides between the steps. Deprecation is hard work, and may seem mostly useless, because it takes away the resources for very little visible gain in the layer’s never-ending pursuit of adjusting its vector of intention. Folks who only measure a team's success in features shipped will view deprecations as a distraction, or at best a “necessary evil.” If an organization's culture was established during the rapid growth of a new-frontier platform, advocating for deprecation can be a tough sell.
When I want to know if a team knows how to evolve platforms, I look for things like the presence of deprecation processes, accompanying telemetry, and deprecation accomplishments celebrated on the same level (or above!) as shipping new features. A special bonus: a platform design itself considers deprecation, and perhaps even offers some novel insight that advances beyond this classic two-step technique. Perhaps a platform moonwalk of some sort?
🔗 https://glazkov.com/2022/03/17/the-platform-two-step/
Solved, unsolved, unsolvable
I have been noodling on a decision-making framework, and I am hoping to start writing things down in a sequence, Jank-in-teams style. You’ve probably seen glimpses of this thinking process in my posts over the last year or so, but now I am hoping to put it all together into one story across several short essays. I don’t have a name for it yet.
The first step in this adventure is quite ambitious. I would like to offer a replacement for the Cynefin framework. Dear Cynefin, you’ve been one of the highest-value lenses I’ve learned. I’ve gleaned so many insights from you, and from describing you to my friends and colleagues. I am not leaving you behind. I am building on top of your wisdom.
This new purported framework is no longer a two-by-two. Instead, it starts out as a layer cake of problem classes. Let us begin the story with their definitions.
At the top is the class of solved problems. Solved problems are very similar to those residing in Obvious space in Cynefin: the problems that we no longer consider problems per se, since there’s a reliable, well-established solution to them. Interestingly, the solution does not have to be deeply understood to be a solved problem. Hammering things became a solved problem way before the physics that make a hammer useful were discerned.
Then, there is a class of solvable problems. Cynefin’s Complicated space is a reasonable match for this class of problems. As the name implies, solvable problems don’t yet have solutions, but we have a pretty good idea on how they will look when solved. From puzzles to software releases, solvable problems are all around us, and as a civilization, we’ve amassed a wealth of approaches on how to solve them.
The final class of problems loosely corresponds to Complex space in Cynefin. These are the unsolvable problems. Unsolvable problems are just that: they have no evident solution. At the core of all unsolvable problems is a curious adaptive paradox: if the problem keeps adapting to your attempts at solving it, the solution will continue being just out of reach. I wonder if this is why games like chess usually have a limited number of pieces and a clear victory condition. If the opponents are matched enough, there must be some limit to make this potentially infinite game finite. Another way of thinking about unsolvable problems is that they are trying to solve you just as much as you’re trying to solve them.
You may notice that there is no corresponding match for Cynefin’s Chaotic space in this list. When describing Chaotic space, I’ve long recognized the presence of a clear emotional marker (disaster! emergency!) that seemed a bit out of place to how I usually described other spaces. So, in this framework, I decided to make it orthogonal to the class of the problem. But let’s save this bit for later.
The interesting thing about all three classes is that they are a spectrum that I loosely grouped into three bands. Obviously, I tend to think in threes, so it’s nice and comfy for me to see the spectrum in such a way. But more importantly, each class appears to have a different set of methods and practices associated with it. You may already know this from our studies of Cynefin. Just think of how the effective approaches in Complex space differ from those in Complicated, and how both are different from those in Obvious.
Still, it is also pretty clear that the transition between these classes is fuzzy. As my child self was learning to tie shoes, the problem slowly traversed across the spectrum. First, the tricky bendy laces that kept trying to escape my grasp (oh noes, unsolvable!?) became more and more familiar, while tying the crisp Bunny Ears knot, despite being clearly and patiently explained, was a challenge (wait, solvable!). Then, this challenge faded, and tying shoes became an unbreakable habit (yay, solved). This journey across the problem class layers is a significant part of the framework, and something I want to talk about next.