The cost of opinion

It appears that I’ve been writing this essay since 2012, never quite finding the right framing. One of my brilliant colleagues asked me this question back then: “How do you evaluate whether a Javascript framework is good for the Web? You seem to do it intuitively, but I don’t get it. What’s the logic?” And I was stumped

Now, a decade later, here is my best-effort attempt to capture that intuition. I am a bit older now, so I offer this with a bit more humility. The story is also a bit more technical in nature and I apologize to my non-technical readers in advance.

I tried to formulate my answer as briefly as I could, and it came out almost like a poem, in four … shall we call them stanzas?:

Frameworks and libraries are like layers,
and these layers accrete.

Every layer has a vector of intention,
pointing toward some idealized value to users,
determined by the author of the layer.

Opinion,
or the difference

between the vectors of intention of two adjacent layers,
always comes at a cost. 

Opinion costs compound
and are, directly or indirectly,
shouldered by users.

Below is the reasoning that went into this little ditty. The first line sets the stage with a simplistic, but hopefully useful framing. Let’s imagine all developer-facing software as something that accretes layers of abstractions over time. These layers of abstraction usually emerge as libraries or frameworks, written on top of some existing layer (I will use the word “platform” to describe it)  – usually to provide additional value that wasn’t there before.

For example, the venerable jQuery is probably the MVP of the Javascript frameworks (and maybe even all developer frameworks of all time): during the times of browser wars and through the winter, it steadily held developers’ hands, providing a decent interoperability layer over the treacherous terrain of grotesquely diverse and buggy browser implementations. If you knew jQuery, you knew how to make things on the Web. jQuery emerged out of necessity and accreted on top of the DOM APIs. All these years later, I still find it on many (most?) sites across the Web. In fact, some (many?) newer frameworks themselves relied on jQuery, accreting a second layer of value on top of it. So, here’s our first stanza of the narrative: frameworks and libraries are like layers, and these layers accrete.

As a second step forward, let’s recall the tale of two models: the “what is” and “what should be.”  The former represents our current understanding of the environment, and the latter – our preference toward its future state, reflecting our intention. Every framework and library is its author’s manifestation of this intention, taking the “what is” of the underlying platform (or layer) and transforming them to produce the “what should be”: it’s own APIs. For example, jQuery took the dizzying variety of ways to find a DOM element across different browsers back then (the “what is”) and produced the now-ubiquitous “$()” function (the “what should be”). Think of this transformation as a directional arrow (a vector!) connecting the two models of environments. The butt of the arrow starts from “what is” and its head points toward “what should be.”

Unsurprisingly, the layer underneath also has an intention. Even bedrock platform APIs do this work of translating something at an even lower layer to something they believe to be more valuable to users. Even though its developers aren’t always conscious of this intention, every layer has one. Embedded in intention, there’s some mental model of what “good” (or “valuable to users”)  looks like. Thus, our second stanza is: every layer has a vector of intention pointing toward some idealized value to users, determined by the author of the layer.

Now, we’re ready for the third hop. When the vector of a layer aligns with the vector of the layer below, we say that the library or framework that comprises this layer is un-opinionated. When it does not, we say that it is opinionated. The difference in the vectors is the degree of opinion. Think of opinion as a change in direction: the author of the lower layer was like, “here’s where it’s going!” and the author of the upper layer went: “Cool story, but actually, I want to try this other direction.” 

A layer’s opinion is not something that just sits on the surface, easy to study and examine. Instead, it’s often subtle and hard to see, only becoming obvious over time. Usually, it’s a bunch of smells in the code of the framework or library. It usually looks like plumbing, like doing extra work to translate and adjust the vector of intention. To name a few off the top of my head, look for things like parsing, caching, and predictive logic, as well as wrapping — especially recursive — of underlying objects as possible hints. The opinion often comes across as treating the underlying platform as hostile, using as little of it as possible — which unfortunately, is sometimes necessary to make something that actually works.

To make this more concrete, let’s examine that “$()” function from earlier in the article. At first blush, it seems mostly un-opinionated, taking a CSS selector as the parameter and loosely doing the work of an existing platform API: document.querySelectorAll. The only opinion appears to be in the name of the function: one convenient character instead of … what? twenty five? However, if you were here during the browser wars, you might recall that IE6 did not support that particular API. So the brave jQuery engineers wrote their own implementation of it! I still remember peeking at that code and being in awe of such a feat. Compared to IE6, the hallowed dollar–sign function was a bit more opinionated. It disagreed with the idea that getElementsByTagName and getElementById ought to be enough for everyone and ventured forth in a direction that was more aligned with the nascent Web standards. And in doing so, jQuery incurred costs – the extra CPU cycles, the extra bytes over the wire. That’s the curious property of opinions. In frameworks and libraries, opinions have cost. Changing the intention’s direction is not free. To articulate this as our fourth stanza: opinion, or the difference between the vectors of intention of two adjacent layers, always comes at a cost.

Once incurred and embedded into the framework, this cost is difficult to give up, even when the platform underneath changes for the better. For example, even though IE6 has gone away, jQuery still carries the darn selector-parsing code, which like any proper barnacle, has grown all kinds of neat optimizations.

The cost is proportional to the degree of opinion. For example, if I decided to build a Javascript framework that completely reimagined UI rendering as graphs or three-dimensional octagonal lattices rather than trees, I would quickly find myself having to reinvent the universe. The resulting artifact will weigh some megabytes and consume some kilowatts, with DOM trees still impishly leaking out of my pristine abstractions here and there, necessitating tooling and various other aids to ensure successful launches of user experiences, built using my awesome framework.

What’s even more alarming is that opinion costs have a tendency to compound. Should developers find my framework appealing, I will see more high-profile sites adopting it, causing more developers to use it, extend on top of it, and so on. The outcome of this compounding loop is that users will find more of their computing resources drawn to chew on my opinions.

Commonly, this compounding effect tends to be indirect and delayed enough that originally, the framework or library appears to be providing a lot of benefit at nearly no cost. Only over time, the compounding cost of opinion swings the net value curve back into the ground — and we’re left with massive debt that swallows us and dims the vitality of the ecosystem around us. Which brings us to the last stanza: opinion costs compound and are, directly or indirectly, shouldered by users.

This effect of compounding costs crosses all layers. If the platform designers came up with the primitives that ultimately don’t satisfy the needs of users, the framework and library developers who attempt to rectify this situation will always incur opinion costs. There is no cheap way to salvage mistakes of the platform designers. Design of the platform primitives matters, because it establishes the opinion cost structure for the entire developer ecosystem.

Applying this lens, it seems that platform developers have the highest leverage for reducing the overall cost of opinions carried by users. This is why platforms are better off not sitting still. Every broadly used platform is spurred to learn how to evolve – even if at glacial pace – toward reducing the overall cost of opinion introduced by developers who layer on top of it. So if you’re designing a new platform, you would be wise to invest into building capacity to evolve into it. And if you’re a steward of a mature problem, your best bet just might be teaching it how to change.

4 thoughts on “The cost of opinion”

Leave a Reply

%d bloggers like this: