I stumbled into this framing when talking about product/platform thinking with my friends. To put this framing together, we have to employ a certain way of thinking about software products – viewing them as a graph.
Imagine a graph – a bunch of vertices and edges that connect them. In this particular graph, bits of software we write are represented vertices, and the formats, protocols, and APIs we use to glue these bits together are represented by the edges. Both are important and necessary.
Represented like this, the two groups (the software and the glue) can be discussed separately. For example, when I talk about an API to get some data (like current time) from a particular server, I can see what appears to be one thing – the API to get current time – as two distinct entities. In our graph representation, there will be the vertex representing the code that is required to obtain current time and format it according to some requirements, and the edge, which represents the actual API contract that defines the requirements to be implemented.
C++ has this separation built into the language: to write useful code, a developer has to produce both the header file (the edge) and the source file (the vertex). Other languages have the construct of an interface that serves a similar purpose, though most of the time, this distinction between edges and vertices is more of a practice. One of the more familiar ones is test-driven development (TDD), where we are encouraged to start by describing the behavior of the code we want to write in tests, and then writing the code itself.
This distinction reveals two different thinking modes that appear to be complementary, but independent of each other: the vertex-wise thinking and the edgewise thinking.
When I have my vertex-wise thinking hat on, I am usually contemplating the actual building blocks of the thing I intend to make. This one is very common and a bit of a habit for software folks: when asked “what are we building?”, the answer shows up as a list or a matrix of things, a to-do list of sorts. “To get <foo>, I will need <bar> and <baz>” and so on. This approach is super-familiar to any maker of things, professional engineer or not. While in this thinking mode, the vertices are at the center of attention and edges are an emergent artifact. We are focused on constructing the blocks, and how they are connected is something we figure out as we go.
The situation inverts with edgewise thinking. When I have the edgewise hat on, I am primarily focused on the edges. I want to understand how the vertices connect – before figuring out what they’re made of. Here, the glue between the blocks is the subject of my attention, and the blocks themselves are an emergent outcome. When asked “what are we building?”, the answers that the edgewise thinkers give usually come in the form of control flow or other kinds of interaction diagrams, defining how the vertices will communicate with each other. Edgewise thinkers obsess about formats and protocols.
One of my mentors had a habit of designing through mocks: sketching out an entire project as a collection of dummy classes that do the absolutely minimal amount of work to glue together. You could build this project and run it, and it would actually print out sentences like “contacting bluetooth stack” and “getting results”, none of it real: it was to be implemented later.
Such edgewise thinking exercises allowed us to see—and mess with—the shape of the entire thing long before it existed. The mocks served as the edges. Once we figured them out, vertex work was just a matter of typing out code that conformed to the shapes the edges defined.
The inevitable question in an article of this sort arrives: which one is better? It depends on the situation. A colleague of mine captured the rule-of-thumb well: edgewise thinking tends to lead to divergent effects and vertex-wise thinking to convergent.
Vertex-wise thinking works exceptionally well – and is called for – when we know pretty well what we’re looking for as the end result, and the biggest obstacle we’re facing is proper sequencing of work. Program managers can do wonders turning lists and matrices of building blocks into burn-down charts and shipping schedules, and help the team click through the milestones.
Edgewise thinking can’t compete with that. In fact, folks who favor edgewise thinking tend to struggle with calendars. However, one of the superpowers of edgewise thinking is the ability to surprise ourselves. If we leave vertices as an emergent outcome, we create the potential for something we didn’t anticipate: different ways of using the edges to build a whole different thing.
For example, imagine that we are Tim Berners-Lee back in the 1980s and we want to organize the documents across departments at CERN. Applying vertex-wise thinking, we end up with a pretty cool database that any CERN member can use. Applying edgewise thinking, we end up inventing the Web.
Platform and ecosystem work is first and foremost edgewise thinking. We start by imagining how various bits will connect and what the value of these connections might be. We find ways to ensure that this value is preserved and increased by designing the edges: interfaces, formats, and protocols that define the connections. We intentionally leave the vertices blank and invite ecosystem participants to invent them by relying on the connection mechanisms we designed.
For me, a really useful marker in a software design conversation is when the notion of “reusable building blocks” comes up. It’s a good sign of a thinking hat confusion: the participants are engaged in vertex-wise thinking when they need to think edgewise. A “reusable building block” is not really a block, but rather the glue that makes it reusable. When in this conversation, it might be good to pause and reorient, switching hats: discuss interfaces, formats, and protocols, rather than the actual code that will fill out the block.