Recap
For those of you who just arrived, this project is about reconciling the work of the recently-deceased architect Christopher Alexander—inventor of pattern languages—with the craft of software development. Patterns were a big hit with us in software, but we didn't keep up with current events—and by current I mean 26 years ago. You see, Alexander personally renounced patterns. Said they weren't good enough. He told his audience he was working on something simpler—much simpler, and much more profound. And that's what we're exploring here.
This essay explores the second of the fifteen properties from Alexander's magnum opus, The Nature of Order. If you don't know what I mean by that, then read the introduction, as well as the first chapter, Levels of Scale.
If Christopher Alexander's latter-career methodology has a centre, it's the center. A center can be understood as a region of space (or spacetime if you like), that is differentiated from its surroundings somehow. A center is identifiable; you should be able to point to it, maybe even name it. A center has thing-ness. It need not, however, have a crisp boundary: centers, according to Alexander, are field-like. This tracks, because they don't call it the spacetime continuum for nothing.
A center, therefore, is kind of like a “minimum viable thing”: any thing is also a center, and if it's not a center, then it's a non-thing. The reason why one would bother introducing a new term center and not just say thing is to call attention, for one, to what I just wrote about the fuzziness of boundaries, but also to further specify that centers are recursive: centers are made of centers, and compose to make up other centers.
If you're versed in design at all, you might have encountered the term affordance from cognitive psychology. An affordance can be understood as a relation between a living being and its environment, or an object in the environment, that governs potentials—what can happen next. A center is somewhat analogous, indeed possibly more primitive: using the language of affordances, we can say that a center affords—at least—noticing, identifying, pointing at, focusing on, moving toward. While a center is always an objective, physical, geometric phenomenon, it has a strong coupling to life through its capacity to carry information.
Dazzle camouflage deliberately disrupts the apprehension of centers, incidentally by doing the exact opposite of the Alexandrian methodology.
What I mean by this is that a center is defined through differentiation from its surroundings. A strong center, in Alexander's parlance, is one that is more sharply perceptible than others. But more perceptible to what, other than a living being? Information is inextricably tied to life; information is meaningless without life. Life is made out of information, to the extent that it is, as Schrödinger argued, a localized elimination of entropy. Gregory Bateson wrote that information (or rather, a bit) is the difference that makes a difference—but again, difference to whom?
Imagine a universe that was almost completely smooth, undifferentiated except perhaps for the occasional ripple. Is anything even there? Before you answer yes, recall that you are a highly-differentiated being, situated in an even more highly-differentiated universe, contemplating an imaginary, undifferentiated universe. If you were in that universe, you'd be nothing but a ripple yourself. And at that point, what does it mean to perceive, other than one ripple colliding into another? How much more robust would you have to be in your differentiation—how much of a stronger center would you have to be—such that this mere act of perception was not a fatal incident? What would it mean to be a being if you were obliterated by the first thing that you ever encountered?
As I wrote in the introductory chapter, Christopher Alexander is criticized for coming off as mystical when he talks about life beyond a strict biological definition. It isn't that outlandish an idea, and is certainly congruent to, for example, the beliefs of the First Nations on the North American continent. Ordinarily speaking, though:
- Life (biological) needs other life to survive, - life is highly differentiated (another way of saying it contains a lot of information) but also highly compressed, through various symmetries, repetitions, and recursions, - this produces a distinctive signature, - it follows that life will go where the other life is, - any region of space where life congregates long enough will become noticeably shaped by it, - this will cause additional (biological) life to notice and continue to congregate there, - which will further take on the characteristics of living structure, and so forth, and so on.
A quintessential example of living structure is a coral reef.
The way that I read this process is, living structure (qua Alexander) is not only a habitat for, but also a giant advertisement for (actual biological) life. The process creates nooks and crannies for plants and bugs and birds and critters and people to come and do their thing.
So the center is the elementary unit of thing-ness, and living structure—whether strictly biologically alive or not—is absolutely crammed with them. But merely cramming centers into a space isn't enough: according to Alexander, the way to get a density of centers found in living structure that are strong enough to maintain their integrity (and therefore not be complete chaos), is a generative, stepwise process of differentiation through structure-preserving transformations.
The main floor of the Martin House, by Frank Lloyd Wright, focuses around a large living area, with a covered veranda that would more than double the space in the warmer months. Business and pleasure are bisected by a long hallway, outfitted with an atrium.
Alexander included strong centers as one of the fifteen properties because it was specifically the strength of centers rather than centers themselves that he was focusing on. Individual centers can be strengthened through other properties/transformations (levels of scale, contrast, local symmetries, etc.), but this property imagines a field effect created by the configuration of many centers, to create a center of centers. So:
- The building is a center, situated in its context. - The building has a center—which is not necessarily in the absolute exact geometric centre. - The prominence of the main center—and therefore the building—is reinforced by conscientious arrangement of subsidiary centers.
The main center of Reims is the altar, which sits at the boundary between the transept and the choir, which is closer to the right of the image (that would be the east side of the building, near the number 47). Note how the five chapels in a semicircular formation strengthen the apse, which in turn strengthens the choir.
We can say the same thing for a good piece of software:
- The artifact is a center. - The artifact has a center. - The artifact's main center is reinforced by the arrangement of subsidiary centers.
“The artifact”, here, is something we can address from multiple angles:
- Its user interface, - its behaviour—that is, its movement from state to state, - and finally, its source code, its precursor materials, and all the conceptual entities those embody.
One remarkable thing about software is that the processes of both making and using it are at least capable of exemplifying the kind of social activity consistent with living structure, without any significant brick-and-mortar elements. You can use or make software pretty much anywhere and in pretty much any social configuration—granted some are more effective than others—because the process is superimposed onto the environment, and resides largely in shared patterns of thought and language. This makes the centers of software almost completely virtual.
One might be inclined to point out that we can look for centers in the source code, but where is the source code? There are almost always multiple identical copies of it, and that's not counting the copies that are actively being worked on. Each copy can be smeared across storage in any which way, and is only reassembled to look at it. The source code is therefore ephemeral. The centers of software, while they necessarily must take a (barely) physical form to have an impact, exist primarily in our minds.
There is a certain category of software application that rose to prominence in the 1980s—although the seeds had been planted much earlier. It's what Alan Cooper termed the sovereign application, so named because it occupies the entire screen, and, prior to preemptive multitasking and endless notifications, the user's complete attention. These applications typically consist of a main workspace or canvas area, with tool and information palettes in the periphery. These applications include professional stalwarts like Photoshop and Illustrator, but also range into general-purpose office productivity tools like word processors and spreadsheets, but also more esoteric tools for media production—3D graphics, editing and post-production for video and sound—as well as scientific and engineering tools.
What we can say about these tools is that they are genuinely tools. They are generally inert and do nothing without the user's intervention. The user has to master the tool, which can take years, though the skills are semi-transferable between competing products. Many of these applications from the 80s (or their successors) are still around, and have users that have been using them since they were new. Each of these tools tends to have an associated file format, and it was a common tactic to keep these formats secret to ward off competitors. Only with the internet did interest in proactively opening data formats really take off. The apps themselves also presented as (mostly) unitary objects. On a Macintosh, they literally were single files. On Windows, they would be (usually) confined to a single directory with a main executable—which is the thing you would open—and whatever resource files weren't embedded inside it.
There are obviously plenty of other genres of software: non-sovereign desktop applications (calculators, virtual sticky notes), mobile and tablet apps, games, Web applications (which could be any of the above), VR and AR apps, operating systems, daemons, compilers and interpreters, command-line tools, libraries, frameworks, and embedded systems, to name a few. But, the question here is “where are the centers in the user interface?” and I'm not going to create an exhaustive list; the goal is to be able to spot centers in any software, and I need a basis to work from.
The icon that you click on to load the program is a center (presumably containing its own centers), and I would argue that by extension, so is wherever that program lives. The files you save and load are centers. The main application window is a center. The canvas within the main application window is a center. The tool and info palettes are centers. The menu bar and its operations are centers. The conceptual entities you manipulate are centers.
Not every aspect of a user interface is graphical, nor is every aspect confined within the scope of the software artifact itself. That said, I am deliberately trying to consider the static structure of the user interface—whether geometry applies or not—separately from dynamic behaviour (whether it is part of the user interface or not). The latter I will discuss in its own section.
How do we get the field effect of strong centers? That's a really good question, and hard to answer if we're mainly considering graphics and bracketing out behaviour. Since pixels are (almost) always square, we generally see a lot of bisecting going on. This could express a separation, with an explicit border, or implied with whitespace. Mobile applications do this a lot. But, mobile applications also tend to debase the main center by making more than one of them. The argument we will likely hear for this is that this is because of the size constraint, but I can think of at least one mobile app that warps and bends its space so that it does not dilute the main conceptual center.
Rudolf Arnheim's book The Power of the Center argues that you can have an object like a piece of art on the floor and it can have a genuine radial symmetry, but the moment you put it upright—like hang it on a wall—it acquires a lateral bias. To the extent that screens are generally held upright, we are likely to see them treated as “one-and-a-half-dimensional”. That is, divided first into an up-down axis, and then into a left-right axis. This is also consistent with the ergonomics of using mouse pointers (or your thumb on a phone), and visual metaphors like scrolling paper in documents. Only in rare situations will a bilateral (i.e., four-fold radial) symmetry be usable.
If we limit our search for strong centers in user interfaces to the geometry of the graphics, we will miss what's really going on with centers in buildings as well. In ordinary desktop apps, tools (i.e., input modes) and operations often reside primarily in the menu system, and then are duplicated in a toolbar, and then duplicated again by a keyboard shortcut. That is, the centers are psychosemiotic: they are an association in your mind between a configuration of space and some unit of meaning. As you gain proficiency in the app, you learn the connections between the different control modalities (menu vs. toolbar vs. keyboard). You can begin to ignore the toolbar meant for novices—or even remove it in some cases to make more room for the work area—and use the much quicker keyboard shortcuts, a pattern that persists from app to app.
We can say roughly that there are two kinds of software executable: the kind that runs and terminates, like batch programs and (most parts of) libraries, and the kind that runs in a loop, like daemons, apps, and operating systems. The latter, moreover, can very often—though not always—be construed in terms of taking the former and just wrapping a loop around it. This initial bifurcation reinforces the two centers looping part and running-and-terminating part, and helps the programmer concentrate on the correct behaviour of either.
It can be argued that ensuring a piece of code always terminates is a decent-sized chunk of a programmer's job—one that we know can't be automated.
The running-and-terminating part—we can call this the handler—can in this configuration be treated as unitary. This is how a Web server (daemon) like Apache operates. What's more, is that a run-and-terminate pattern can be further constrained in terms of what it accepts and emits, by casting it as a pure mathematical function. Contrast this with an app, for which input events typically act like surgical tools that come in and operate on some persistent state object. Or an operating system kernel, which unceremoniously chops up bulk computation whichever way is most convenient. The functional paradigm, on the other hand, enables the programmer to think about these run-and-terminate procedures as a series of tubes—if you'll allow me the indulgence—and a neat little assortment of filters that can be combined together in all kinds of useful and creative ways.
The looping part of a daemon is typically quite simple: upon an input event, run the handler, and then return to the home state. This, and much more complex behaviour, can be modeled as a state machine. Each state is of course a center, and any transition from any state S to state T is also a center. This forms a directed graph, and will naturally contain substructures at different levels of scale. Specifically, we will see clusters of states with many arrows between them, and only a few leading in or out. We can draw a border around them and treat them as a single center. Likewise, if we record the execution of the software long enough, we'll see that some states get visited much more frequently than others, making the individual states themselves effectively “bigger”, and often more important.
This brings me to websites. The process of browsing the Web is exactly equivalent to a state machine. A Web page is less of a document, and more of a state. The geometry of the page, and its location in a document hierarchy (a completely unnecessary vestige of early technical expedients), nevertheless still often dominates the design, rather than the path taken from one page to another. To wit, the home page of a website is often given lavish attention, despite it being more akin to the front door of a house than the living room.
We can see so far that strong centers looks a lot like separation of concerns, but only partially. I'll demonstrate with a humble example from my own repertoire.
I have benefited a great deal in my work by solving the mundane but very
concrete problem of fitting a bolt, which I'll call UUID, into a
socket, which I'll call NCName
. Ordinarily, UUIDs don't fit into
NCName
s, so the problem was to change the shape of the UUID so it
fits.
At the centre (and center) of this problem is an algorithm, or rather two: one that changes the shape of the UUID, and another one that changes it back. This is one logical center which subdivides into two complementary centers, which each internally look like a procedural mirror image of the other.
Some, furthermore, may not be aware that a UUID is not just a random
string of bytes, but actually has a structure (five different kinds and
counting). Namely, in a
conforming UUID, there are two characters that will always take the
same set of values. The standard UUID representation—a knobbly kebab
of hexadecimal digits skewered by hyphens—is regular enough that
automated systems can scan for them and safely assume they refer to
something. However, this is why the UUID plug doesn't fit into the
NCName
socket: a UUID more likely than not to start with a number,
and the NCName
grammar doesn't allow that.
So the challenge was to preserve the recognizability of the UUID
while making it conform to the grammar, and pick up a few bytes in the
representation on the way. My solution was to move the UUID's version
component to the front of the symbol, where I can make it always start
with a letter, A
through (potentially) P
. For symmetry's sake, I
moved the other component to the other end of the symbol. This one in
practice is masked to always take decimal values 8, 9, 10, and 11, will
always take the values I
, J
, K
, or L
. In the middle of the
symbol, I encode the rest of the UUID's content, for which it was
possible to specify three variants:
- Shortest-possible representation: EDUZ-7OYhjDPif7empGN_K
- Slightly longer, but no hyphens (important for some contexts):
ENYQMKcwPq6hWT2JiC1LA_K
- Least compact but case-insensitive: ebvdh53hgeggdhyt7w6tkiy37k
These representations, along with the original
0d467eec-e621-48c3-a3e2-7fb7a6a4637f
, are all equivalent. The
“bookend” characters, which are
always the same for each variant, play an analogous role to the original
hyphens, insofar as they help recognize these symbols.
Of the five extant UUID versions, only 1, and the almost-never-seen 2 actually make use of the five-component anatomy of the canonical representation. The chief elements of interest for these will be a timestamp and (what is usually) the MAC address of the primary network interface of the machine that generated it. That information isn't gone; you can always convert the UUID back to its original (or more appropriately, its binary) representation to recover it. I judged this relatively uncommon scenario to be an acceptable trade-off for my goal.
I chose this project as a demonstration because despite its extremely small footprint, it is still brimming with centers. The representation itself has, off the top of my head, levels of scale, alternating repetition, contrast, boundaries, and echoes. There are also centers where one would expect them: in the layout of the source tree, and in both the literal and logical structure of the code. I also implemented a pattern I often do for these kinds of projects, which is to include a command-line tool, which is typically a thin wrapper around the library. This, along with the test suite, comprise three centers that reinforce and complement each other nicely. Finally, since this software is dealing with data that it ultimately doesn't control, it is not satisfactory to confine it to a single implementation. So I have two (and counting) implementations in different languages which echo each other in structure, and a central specification that yokes them together, which I endeavour to turn into a standard.
Ferrofluid—iron dust suspended in oil—takes on the elaborate shape of the field of any magnet placed close enough to it.
Software has little choice but to be living structure, because, as I argued in the introductory chapter, it shrivels up and dies✱ without attention. I demonstrated above that there are centers everywhere in software (even in software without what we would normally consider to be a user interface), but like a magnetic field or radio waves, many of them are hard to see without instrumentation.
✱ There are worse fates, come to think of it. “Legacy systems” are the undead software that people still depend on to do something essential (poorly), nobody knows how they work, and the people who wrote them are long gone.
For starters, software contains no shortage of graph structures: we have inheritance graphs, dependency graphs, call graphs, etc. Version control systems are a graph of branches and commits. Bug trackers have comment threads and dependency graphs too.
This is a Graphviz rendering of the grammar of an older version of the Ruby programming language.
All of these graphs, save for the call graph, can be obtained just by looking at the body of work product. For the call graph, you'll have to run the code in a profiler. As for the question of what precisely you run in profiler, this is as good a justification as any for good coverage in a test suite.
Graph visualizers have been around for decades, invented for the express purpose of surfacing structures like dependency and call graphs. Puzzlingly, though, these tools are ostensibly considered relatively esoteric, and not an essential part of the development process.
On the subject of tests, we have an opportunity for a field effect: the test—or as the behaviour-driven development folks like to call it, executable specification—complements a discrete piece of functionality. These, when taken together, have another mirror image: a prose specification, and implementation rationale. If you haven't heard of an implementation rationale before, I'm talking about the remarks that explain things like why you chose one particular approach over another one; things that generally don't get communicated by the code itself. To the extent that implementation rationale exists, it can usually be found in the code's comments.
Normative Descriptive Prose Specification Rationale Code Test suite Implementation A discrete unit of software deliverable easily splits into four pieces along two axes.
To support the development of his TeX typesetting system, Donald Knuth invented a style of programming which he called literate, along with a supporting toolchain. Literate programming is based on the following tenets:
- Humans and computers have sharply divergent priorities when it comes to the representation of source code. - Source code—assuming it is intact—can always be transformed into a representation more amenable to the computer, so therefore it should be structured initially with humans in mind. - Copious expository text helps humans understand what the computer is being told to do. - Elucidating the code's behaviour in prose leads to fewer programming errors and, somewhat counterintuitively, a net saving of effort.
Knuth's tools would take a single, unitary input file containing interleaved prose and code, and and turn out two variants: a beautifully-typeset manual, and a sanitized, rearranged implementation that can be passed downstream to the compiler. If you look at Knuth's output though, the prose mainly narrates what is being done in the code. If he hadn't started his career decades prior to a strong culture of automated testing, we could imagine tooling that creates the fourfold split depicted above, instead of just the two.
To the cynical manager, the part that earns the money—the implementation—can operate without documentation, tests, or even comments. This leads to a casting of these artifacts as “extra work”, and therefore ultimately amenable to being sacrificed for the sake of the schedule✱. What is interesting about literate programming, and worthy of study, is that it centres documentation (and, conceivably, tests) and makes the formal language of source code an elaboration of that. To flip back to Alexander's verbiage, Knuth's configuration exhibits more wholeness, because it yokes together parts (centers) that belong together.
✱ It does not take very long into one's career to experience just how counterproductive this situation is, and yet, it is utterly pervasive.
I want to close this chapter with a final remark that almost everything I've written on this subject so far narrowly considers code, while there invariably exist several layers of precursors that precede it. Every subroutine we write, for instance, will eventually connect to some goal that is desired by some archetypal user. These are centers too, which can and should be modeled. As I likewise demonstrated above with the UUID anecdote, there are always ecological considerations: some subset of the software always has to interact with the world around it, even if what it interacts with isn't a person.
Centers in buildings and other physical objects are a geometric phenomenon, but their effect on us is semiotic. To see the centers in software, we have to start with the signs, and work our way back to the structure. The field effect created by strong centers can be dimly perceived by the experienced or particularly astute observer, but it jumps to life when seen through the right lens. While this series is intended to be descriptive, I'll conclude here with a bit of advocacy: we should be investing in tools and infrastructure to help us see the centers in—and around—software; it will help us to create more harmonious, comprehensible systems.