The language frames the story

Suppose you’ve said to me that “the falling maple leaves whirl in a delicate dance with the gentle breeze.” It’s late fall, so it feels like a reasonable thing to say. The trouble is, my language only has words like “eat, fight, run, here, that, there, now, later.” I might be fascinated by what you’ve said, but get very little out of it — and even less to pass on to others. At best, I will point at the sky and say “that!” and bulge my eyes meaningfully.

This might seem like one of those — duh! — obvious observations. Of course the language frames the story. However, I am finding that I easily forget this in the contexts of teams and how they are organized. To communicate with each other, teams establish a common language, a set of semantic shortcuts that allow them to work together effectively. This is where the non-obvious bits hide. This common language also frames what is being communicated.

For example, let’s imagine a hypothetical organization where communicating strategy across teams is done through a list of annual objectives and key results (OKRs). There is a process to assemble them into a coherent whole. When it’s all said and done, the story written in the language of OKRs tells where we will go next year.

However, some of our desired destinations take longer than a year to reach. To communicate these destinations, we need a language that can describe ideas that last longer than a year. Can you guess what happens when we try to express them in the language that only speaks in year-long sentences? Sure, within teams there might be long-term thinking and sound strategic artifacts. However, given the pidgin of annual OKRs that prevails outside, it is unlikely that these artifacts survive cross-team communication. The multi-year ideas will be neatly chopped or scrunched into the one-year box — rendering them as useful as my eye-bulging-while-pointing.

If I am lucky enough to work in an organization full of brilliant individuals, I will also start seeing these folks adapt to the common language. More and more, I will see how they pick out ideas that fit within the language to reduce the friction, making sure that only year-long journeys are those that count, whether in impact or attention of leads. Sure, there will still be the linguistic idealists who rail helplessly trying to overcome the limits of the OKR pidgin, but those will eventually give up and leave.

While the organization wonders why it struggles to think strategically, it might be worth it to examine the language it uses to organize itself. Is the language flexible enough to accommodate both short-term and long-term destinations? Are all ideas that matter expressible in the semantic shorthand that everyone is expected to speak?

The developer funnel

If I ever chatted with you about developer experience in person, I’ve probably drawn the developer funnel for you on the whiteboard. For some reason, I always draw it as a funnel — but any power-law visualization can suffice. As an aside, I do miss the whiteboards. 

At the bottom of the funnel, there are a rare few who know how to speak to computers as directly as humans can. Their perspective on writing data to storage involves programming a controller. As we travel a bit higher, we’ll find a somewhat larger number peeps who write hardcore code (usually C/C++, and more recently, Rust)  that powers operating systems. Higher still, the power law kicks into high gear: there are significantly more people with each click. Developers who write system frameworks are vastly outnumbered by developers who consume them, and there are orders of magnitudes more of those who write Javascript. They are still entirely eclipsed by the number of those who create content.

With each leap in numbers, something else happens — the amount of power passed upward diminishes. Each new layer of abstraction aims to reduce the underlying complexity of computing and in doing so, also collapses the number of choices available to developers who work with that layer. Try to follow the rabbit trail of setting a cookie value, a one-liner in Javascript — and a massive amount of work in the browser that goes into that. Reducing complexity makes the overall mental model of computing simpler and easier to learn, which helps to explain the growing number of developers.

This funnel can be a helpful framing for a conversation about desired qualities of the API. Do we want to have rapid growth? Probably want to be aiming somewhere higher in the funnel, designing convenient, harder to mess up APIs. Want to introduce powerful APIs and not worry about the sharper edges? Expect only a small number of consumers who will understand how to use them effectively.

Developer surface

I mentioned this concept before, and I feel like it’s worth expanding on a little bit. If we are in the business of making a product that developers rely on to create user experiences, the developer surface of this product is the union of all means through which developers create these experiences. 

Let’s unpack this, starting with a simple case. Suppose you and I decided to ship a library that has one function. Applying the definition, that library is the product and its developer surface is the function. Easy, right? As our product becomes popular, we start noticing something weird. Remember that one-line file where we track the version of the library, just for ourselves? Well, turns out some developers started using its contents in their build. So when we thought — “oh hey, let’s just delete that file, we don’t need it anymore” — all hell broke loose? That file became developer surface, too!

In mature developer-facing products, the developer surface becomes far more than just the API. Shipping samples along with the library? Yep, these are part of the developer surface, too. Got some clever heuristics deep in your code? Or maybe just bugsHyrum’s Law captures beautifully the spirit of this phenomenon: 

With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system
will be depended on by somebody.

This sufficiently high number of API users can truly mess with what is or is not a developer surface. While we imagine the contract with developers as a crisp document of high transparency and clarity, we are usually mesmerized by the messy innards that are the outcome of developers just trying to get things to work. Even messier in comparison will be our attempts to convince developers to use the APIs the way we intended.

When we embark on a project that intends to ship a developer-facing product, it’s worth planning the work and structuring the team in a way that anticipates this messiness. We are not writing the developer contract. Developers write the contract with us, and frequently, their contributions carry more weight. Walking this line of carrying our original intention while having awareness of where the developers want to take is not something that comes intuitively or easily.

The story of belonging

Next in my adventure across the coherence narrative realm is the story of belonging. It sort of maps into one of the fundamental needs from the Four Needs Framework, but plays a subtly different role here.

The story of belonging is also something that is easy to spot as a felt experience. Someone wise suggested that humans are wired for connection, and the web of these connections is what creates the gravitational pull in the story of belonging. There’s something about being together with others, being part of the group, next to those who consider you kin, being loved, included, and understood.

When I try to examine stories of belonging, I notice something interesting. They usually contain the words “we,” “community,” “together,” “teamwork,” “alignment,” “unity,” and so on, but structurally, it’s almost always not a standalone story. Instead, the story of belonging acts as a powerful catalyst, laced into the story of a threat or the story of an opportunity. Just milling around together is one thing, but something magical happens when the “we are”  is combined with “under attack” or “the future”: the capacity for coherence shoots up like a rocket. No matter what kind of compounding loop we might face, it seems that doing it as a tightly knit group feels natural and right to us humans. 

To add to the weirdness, the loss of belonging is a common story of a threat — and finding it a common story of an opportunity. In our interconnected world, this kind of ouroboros is in itself an abundant source of compounding loops. It’s one reason why teams tend to form boundaries and organizations grow silos. It is also the undercurrent behind the craving to gain more likes or followers and many similar societal dynamics. 

Considering the story of belonging as means to improve organizational coherence, a couple of thoughts come to mind. First, the stories of belonging are quite inert by themselves. The need to combine with compounding-loop stories to get traction. Second, we live and tell multiple stories of belonging simultaneously. Navigating all these stories is tricky, and growing new stories of belonging (“we are a team!”) is a careful, finicky process that often bumps against all the participants’ stories, taking patience and time to develop. Maybe this is why we use the word “culture” to describe a mature organization’s story of belonging?

Veering toward first-order effects

Have you ever driven a car that pulls to one side? It’s often subtle, but after a while, the counter-steering effort becomes impossible to ignore. This metaphor comes to my mind whenever I encounter a common developer experience pattern: the veering toward first-order effects.

To set the context a bit more, let’s arrange the effects of producing developer surfaces in two orders. The first-order effects relate to producing the developer surface. When we ship an API, we want it to be adopted, to be used broadly. Thus, when we measure first-order effects of our efforts, we look at the API adoption rate, developer satisfaction, etc.

The second-order effects relate to developers producing user experiences using our developer surface. At the end of the day, an organization that invests into shipping APIs does so — intentionally or not — to influence the overall state of user experience in some way. When we measure second-order effects, our metrics will likely track changes in the user experience. Does using our APIs result in products that are more secure, performant, accessible, etc. for the user?

Based on what I’ve seen working with developer experience teams throughout my career, there’s a pronounced pull toward first-order effects. They are easier to measure, have a shorter feedback loop, and are more familiar to folks accustomed to shipping consumer products. Even if a team sets out to influence the state of user experience at the beginning of their journey, the appeal of relative immediacy of first-order effects is so strong that the original intention often gets left behind.

A common symptom of forgetting to counter-steer toward second-order effects is the loss of strategic flexibility within a larger organization. When the first-order effects become the means onto themselves, they tend to get entrenched in a local maxima of developer expectations, stuck in an optimizing loop. An organization that contains teams stuck in that particular way feels like it is unable to do anything about it: everyone is seemingly doing “the right thing,” and prioritization exercises quickly devolve into peanut buttering. When something like this is happening, it’s a good hint that the concept of second-order effects got rolled into a dusty corner of the team’s shared mental models space, or ejected altogether. 

To counter-steer, organizations must exert conscious effort to keep second-order effects in the shared mental model space. Whether it’s constantly pointing at them during the all-hands, setting up the metrics structure to reflect user experience shifts, or even just reminding about the unyielding force that — like that darned car — never quits pulling, it’s an investment that’s well-worth the price.

Cutting or highlighting?

Talking with one of my wise colleagues, we arrived at this neat framing around making decisions. “Decision” is a weird word. My friend Neel tells me that its Latin root is literally “to cut away,” and that recognition that a decision is always about narrowing the list of available options can be both liberating and anxiety-inducing. The interesting bit is that sometimes, decisions aren’t meant to cut away.

As it often happens in larger organizations, we tend to live in the swirl of the short-term and long-term objectives. And it is definitely a swirl: the art of leadership is picking out the right mix. Blend in too much short-term, and we’ll find the team stuck in the corner of a local maxima, overconstrained by its previous choices. Blend in too much long-term, and the team fails to make progress that’s necessary to keep that motor running.

To adjust the mix, leaders decide. A common mechanism here is prioritization: picking a shorter list of things that the team needs to focus on. One approach to prioritization is to apply the cutting mindset, as in cutting the list in half: keep what’s above the line and discard the rest. For example, let’s suppose I want to build an app that is available on both Android and iOS phones, but I want to prioritize iOS users. Applying this cutting mindset, I just forget about Android for a while, break out my XCode and start typing some Swift. Only after the iOS version is shipping do I start looking at the rest of the list. Given how many unexpected turns a typical software project takes, will I ever get to do that? Maybe. Will this approach result in a painful migration or two — or worse yet, the “release polka” where my app always looks out-of-date on one platform compared to the other? Probably.  

The cutting mindset is straightforward and clarifying. Yet, in situations where the rest of the list is still a thing we want to do later, it often leads to inferior choices. In these situations, we need something different. Instead of cutting the list, we want to highlight the items on which we want to focus — while still keeping the rest of the list in mind. With this highlighting mindset, the choices we make take on a different spin. Instead of asking “what’s the next step to deliver <prioritized item>?” we ask “how can we take a step toward delivering <prioritized item> that also takes us closer to completing the whole list?” The thing is, the items on our lists are rarely orthogonal and live in clean separate boxes. More often than not, considering them as a whole reveals opportunities for advancing toward completion of multiple items simultaneously. In my app example, while still focused on the iOS release first, I might consider picking a UI framework that also runs on Android, or at least build my middleware in a way that is portable.

When making prioritization decisions, it’s worth being explicit about the mindset with which you’re approaching, discussing with the team the reason you chose one over the other. Otherwise, despite your desire to highlight, an eager PM might swiftly cut your long-term objectives out of the mix. Or conversely, the team will continue to swirl aimlessly in the ideals you have already forgotten about, from back when you thought you cut them away.

Making cross-cutting investments

Over the last few weeks, I’ve been thinking about approaches to influencing projects and teams across a large organization. I framed the exercise like this: suppose I have some organizational currency (headcount!) to invest. How do I nudge project trajectories in the most effective way?

Then, I talked to a bunch of peeps, looking for forces present in this problem space. There are probably many more, but these two piqued my attention. The first one has to do with the term of the investment: do I want to invest in several different things over time or do I mostly want to keep investing into the same thing? The second one has to do with how much control I want to have over the structure of investment: how much steering do I want to do with my investment? Mapping these two forces into thinking space, a few recognizable clusters emerge. 

A good example of low-control, permanent investment is donation. I find a team, recognize that it is doing important work and decide to help them by adding to their capacity to hire new folks. Based on my experience, this is more or less a permanent investment. Withdrawing donated headcount tends to be painful for all parties involved. Nevertheless, if the team’s goals are largely aligned with mine over the long term, and I have no qualms with their strategy, it’s a pretty good fit.

If I want a more temporary engagement, I need a different approach. One is to temporarily augment a team with a group of folks to accelerate a particular aspect of work. It’s exciting to imagine that such a team will drop in and race forth with uncanny precision. However, in orgs that have strong engineering practices and structures, augmentation is first and foremost a matter of learning to follow those practices and fitting into existing structures. “Who will review your CLs?” is the question to contemplate when considering the augmentation setup. They work well in homogenous organizations, when the members know the engineering practices well and are the people who can review CLs. Otherwise, this investment tends to offer less control than anticipated.

To gain a bit more control without permanence, I will likely try to incubate a team: seed it with good peeps, set up a resilient structure to help it stay on course, get it past the early growing pains, and let it go. Variants of this approach are found in research organizations and idea incubators, and I’ve seen it work. In the couple of times that I participated in the process, the biggest challenge was finding the right fit for the graduating team and then shepherding the team through often painful reintegration. At least to me, incubation felt more like an art rather than a repeatable process, but that just might be the lack of experience.

Finally, if I am seeking to invest in the long term while retaining high control, I am probably productizing, or reframing my desire to help in terms of a developer-facing product: a tool, a library/framework, an SDK, etc. This product must be good enough for the teams to want to rely on — and to get results that I want them to get. Note that this end result is a second-order effect (first, they want to use it, second, they produce desired outcomes), which is what makes this approach so challenging. On the other hand, precisely because of the indirection, this approach has something that no other approaches offer: the ability to influence multiple teams. Productizing is typically more demanding compared to others. It takes more effort and capacity to build an effective team that reliably ships a successful developer product and have the resilience to keep an eye on the outcomes I need. That last one is important. It takes just a little bit of stress and firefighting to fall back into the “let’s make developers happy” mode and forget the whole point of the exercise.

Heuristics will be discerned and codified

Here’s another developer experience pattern that I’ve noticed. When designing APIs, we are often not sure how they will be used (and abused), and want to leave room for maneuvering, to retain a degree of agency after the API is in widespread use. One tempting tool we reach for is the use of heuristics: removing the explicit levers to switch on and off  or knobs to turn from the developer surface and instead relying on our understanding of the situational context to make decisions ourselves. Unfortunately, when used with developer surfaces, heuristics tend to backfire. Developers who want those levers and knobs inevitably find ways to make them without us. And in doing so, they remove that agency that we were seeking in the first place.

Because heuristics are so tempting, this pattern is very common. Here’s the most recent example I’ve stumbled upon. Suppose you are a Web developer who wants to create an immersive experience for their users and for that, you want to make sure that their device never goes to sleep while they are in this experience. There’s an API that enables that, called the Wakelock API. However, let’s imagine that I am a browser vendor who doesn’t want to implement this API because I might be worried that the developers will abuse it. At the same time, I know that some experiences do legitimately call for the device screen to stay awake. So I introduce a heuristic: stay awake if the Web site contains a playing video. Great! Problem solved. Except… You want to use this API in a different scenario. So what do you do? You discern the heuristic, of course! Through careful testing and debugging, you realize that if you put a tiny useless looping video in the document, the device will never go to sleep. And of course, now that you’ve discerned the heuristic, you will share it with the world by codifying it: you’ll write a tiny hosted API library that turns your hard-earned insight into a product. With the Web ecosystem being as large as it is, the library usage spreads and now, everyone uses it. Woe to me, the browser vendor. My heuristic is caught in the amber of the Web. Should I try to change it, I’ll never hear the end of it from angry developers whose immersive experiences suddenly start napping.

It’s not that heuristics are a terrible tool we should never use. It’s that when we decide to rely on them in lieu of developer surface, we need to anticipate that they will be discerned and codified — sometimes poorly. This means that if we wanted to rely on heuristics for some extra flexibility in our future decisions, we’re likely to get the opposite outcome — especially in large developer ecosystems.

Hosting and hosted API design perspectives

When discussing API design strategies, I keep running into this distinction. It seems like a developer experience pattern that’s worth writing down.

Consider these two perspectives from which API designers might approach the environment. The first perspective presumes that the API implementation is hosting the developer’s code, and the second that the API implementation is being hosted by the developers’ code.

From the first perspective, the API designer sees their work as making a runtime/platform of some sort. The developer’s code needs to somehow enter a properly prepared environment, execute within that environment, consuming the designed APIs, and then exit the environment. A familiar example of designing from this perspective is the Web browser. When the user types the URL, a new environment is created, then the developer’s code enters the environment through the process of loading, and so on. Every app (or extension) platform tends to be designed from this perspective. Here, the developer’s code is something that is surrounded by the warm (and sometimes not very warm) embrace of the APIs that represent the hosting environment.

When I design APIs from the second perspective, the developer’s code is something that hosts my code. I am still offering an API that is consumed by someone else, but I don’t set the rules or have opinions on how the surrounding environment should work. I just offer the APIs that might be useful. Typically, this perspective results in designing libraries and frameworks. For example, I might write a set of helper functions that provide a better handling of date math in Javascript. This tiny library can run in any Javascript environment, be that server or client. It can be hosted by any app or site that needs date math. This “run wherever, whatever” is a common attribute of this API design perspective.

There is the growth/control tension that seems to map into these two perspectives. Hosting perspective exhibits the attitude of control, while hosted perspective favors the force of growth. As with any tension, complexity arises along the spectrum between the two.

A Javascript framework (a hosted API) that has strong opinions about its environment (wanting to be a hosting API) will have challenges maintaining this environment, since it is ultimately incapable of creating it. Back in the day when I still worked in the Web Platform, I’ve had many discussions with framework authors who wanted us Web Platform folks to give them the option to create clean environments. This desire to shift from hosted to hosting was not something I recognized back then and now wish this article existed to help me reason through the struggle.

Similarly, a hosting API that wants to grow will be pressed to make the environment more flexible and accommodating. Going back to the example above, we Web Platform folks were experiencing that pressure, the force that was pulling us away from hosting and toward a hosted API design perspective. After that shift, the code that renders Web pages — the fundamental building block of the Web Platform environment — would become just one of the libraries to pick and choose from.

It is also important to note that, using Hamilton Helmer’s classification, the existence of a hosting environment is a form of cornered resource. It’s something that only becomes possible to have when the API designer has the luxury of a significant quantity of willing hosted participants. In the absence of eager hordes of developers knocking on your door, taking a hosting API design perspective is a high miracle count affair. When thinking about this, I am reminded of several ambitious yet ultimately unsuccessful efforts to “create developer ecosystems.” There are ways to get there, but starting out with the hosting API design perspective is rarely one of them.