the anatomy of an explanation
Previously, I provided a brief introduction to the ideas that will be covered in this newsletter.
Software development can be reduced to a single, iterative action. Almost everything we do in the course of a day — the pull requests, the meetings, the whiteboard diagrams, the hallway conversations — is an explanation. Our job is to explain, over and over, the meaning of our software: what it is, and what we expect it to become.
There are three parts to these explanations, and only one is said aloud.
The prefix is what everyone already knows. It is the shared context, the foundation upon which our explanation rests.
The suffix is all tomorrow's explanations. It is everything that, for now, no one needs to hear.
The content is everything in between. It is what our audience needs to know, but doesn't.
To judge an explanation, and to improve it, we must understand all three parts. Let's consider each in turn.
the prefix
In any conversation, most things are left unsaid. When talking to a friend, we don't begin by reciting the details of past conversations. When talking to a stranger, we don't begin with a review of social norms. But these unstated things are, nevertheless, important. They have explanatory power for our conversation; they make our words less surprising.
Similarly, we don't begin every discussion of our software by describing our company's product and business model. These are well-understood; they are baked into our audience's expectations. They are part of the unstated prefix.
Our explanations rest upon these prefixes. Changes can be enormously disruptive. Consider what happens when a company pivots to a new business model: each past decision must be reexplained, to see if it still makes sense.
We can separate any prefix into two parts: the explicit and the tacit. The explicit prefix is the part that can be articulated. It is the things that we have said, or written down, or drawn on whiteboards. The tacit prefix is the part that is inarticulate; the things that we know, but struggle to say. It is the skills and intuitions that we only know how to convey through repetition.
Our industry is awash in tacit knowledge. The Python community, for instance, aspires to write "Pythonic" code. Such code "seems natural to proficient Python programmers"1; in other words, it fits their expectations. And this proficiency, by all accounts, develops over time; it cannot be taught, only learned.
As a rule, an explicit prefix is easier to teach, easier to discuss, and easier to change. In practice, however, our prefixes remain overwhelmingly tacit. Our expectations arise from an admixture of habit, mimicry, and half-forgotten arguments.
And this is for good reason. The leap from tacit and explicit is uncertain; we might not stick the landing. As Michael Polanyi explains it:
Repeat a word several times, attending carefully to the motion of your tongue and lips, and to the sound you make, and soon the word will sound hollow and eventually lose its meaning.2
This is a failed explanation. We have taken something familiar and made it foreign. And unless we have some special insight into phonetics, it's the only outcome that can be expected.
This is why Pythonic code remains a tacit concept. The cost of explicit knowledge is uncertain, unbounded. There are, invariably, a dozen better ways to spend our time.
A tacit prefix is harder to teach, harder to discuss, and harder to change. But it is still, in most cases, simpler than the alternative.
the suffix
As software designers, we spend a lot of time thinking about the future.
Consider how difficult it can be to evaluate a software framework. Each framework provides a set of concepts, and you need to decide if those concepts have explanatory power for your application. Most people start with a small prototype, to ensure that simple problems have simple solutions. But this isn't enough; what you want to know is if the simplicity will last.
As our software grows — in size, in capability, in complexity — we will sometimes need to color outside the lines. We will need to work around the framework rather than within it. And each time we do, the framework will lose a bit of its explanatory power.
To judge one explanation, we must consider its effect on future explanations. The cost of a framework is proportional to its workarounds. The cost of a workaround is proportional to how often it must be re-explained.
The suffix reflects all of this; it is everything we expect to explain. It is our feature roadmap. It is the sharp edges in our code. It is the implementation details we will, eventually, have to understand. It is what, with each design choice, we are trying to simplify.
the content
In between the prefix and suffix, we have the actual content of our explanation: the words, the code, the gestures, the diagrams. Simple content builds atop the prefix, and towards the suffix.
Content, then, conveys more than itself. The prefix is reflected in what it takes for granted. The suffix is reflected in what it values; whatever we have today, we'll probably want more tomorrow.
The prefix, suffix, and content are coupled. They are distinct, but not separable. By changing one, we affect the others.
In this newsletter, we will look at the relationships that bind our explanations. But more importantly, we will delve into the nature of coupling itself. We will try to understand when it hurts, when it helps, and how to tell the difference.
-
Ramalho 2015, p. 748 ↩
-
Polanyi 1966, p. 18 ↩
This post is an excerpt from my (incomplete) book on software design. For more about the book, see the overview.