In 1965 Christopher Alexander showed that living structures overlap, and that the strict hierarchies we impose on them quietly kill what makes them work. The same amputation of organized complexity is built into almost every software coupling metric — including, until we looked, our own.
Christopher Alexander was an architect and mathematician who spent his career trying to say, precisely, what makes some built environments feel alive and others feel dead.
In his 1965 essay A City Is Not a Tree, he distinguishes two ways of organizing a set of parts into groups.
In a 'tree'A grouping where any two sets are either nested (one wholly inside the other) or completely separate. Never partly overlapping. A file-folder hierarchy is a tree. -- the strict hierarchy from graph theory that is only superficially related to a genuine tree -- whenever two groups share a member, one group must contain the other entirely. Groups never partly overlap.
But in a semi-latticeA grouping where two sets are allowed to overlap without either containing the other. The mathematical structure of any living, richly-connected system. It is a type of Directed Acyclic Graph., two groups are allowed to overlap — to share members without one swallowing the other.
That single difference, Alexander argued, is the difference between the artificial cities, which planners draw and impose on the world, and living cities that have grown naturally. In a real city, the corner drugstore beside the traffic light belongs at once to the system of “errands on the way home” and to the system of “this residential block” and to the system of “shops along this avenue.” Those systems overlap in that one place. A planner’s diagram, drawn as a tree, forces the drugstore under exactly one parent — and in doing so prevents the very overlaps that made the corner a place.
When we think in terms of trees, we are trading the humanity and richness of the living city for a conceptual simplicity that benefits only designers, planners, administrators and developers. Christopher Alexander, 1965
His target was 'rational' urban planners who were destroying cities. But the mathematics is about grouping anything — and a modern codebase is, similarly, parts organized into groups.
Below is one small system of components — call them modules in a program. The connections between them never change. What changes is how we group them. Toggle between the two organizations and watch what the tree is forced to throw away.
In the tree, two modules that genuinely serve two groups at once — a shared value type, a logging concern, a utility every subsystem leans on — must be assigned to one parent and only one. The connections that don’t fit the chosen hierarchy don’t disappear from the running program; they just disappear from the picture. The semi-lattice keeps them, by letting the overlap zone belong to both groups at once. That lime region is Alexander’s drugstore at the traffic light.
Why do we keep drawing trees when the world is more like a semi-lattice?
Alexander’s answer is that a tree is easy to hold in the mind. And there’s a well-known reason minds prefer it: Miller’s 7±2George Miller’s 1956 finding that human working memory holds only about seven items at once. We cope by chunking — grouping items into a few clusters, then grouping those clusters, and so on.. We can only keep a handful of things in working memory, so we cope by chunking — grouping items into small sets, then grouping the sets into small sets, recursively.
Chunk recursively and you get a strict hierarchy automatically: a 'tree', by construction. It’s comprehensible precisely because it forbids overlap — you never have to hold two cross-cutting memberships in mind at once. But that comprehensibility has a real cost. Every real relationship that crosses the chunk boundaries is discarded so the diagram stays clean.
This is the crux for software. When we impose a package tree, a layer diagram, or a partition of classes into disjoint clusters, we are chunking. And we are — usually without noticing — discarding exactly the cross-cutting structure that Alexander says carries the life of the system.
Here is the uncomfortable part. Our overcoupling work builds a dependency graph and runs a community-detection step to find a codebase’s “natural” structure. That step uses the Louvain methodA fast, widely-used algorithm (Blondel et al., 2008) that finds community structure by maximizing modularity. Critically, it returns a partition: every node in exactly one community., which returns a partition — every node placed in exactly one community.
A partition is a tree of depth one. By detecting only disjoint communities, our pipeline was structurally incapable of seeing an overlap even when one was really there. We built a tool to measure organic structure — on a foundation that assumed the structure was unnaturally mechanical.
This isn’t fatal, but it’s a real blind spot, and it distorts one metric in particular. Our BVC gapBoundary-Violation-Crossing gap: compares the boundaries a programmer declared (class/module lines) against the boundaries an algorithm detects. A negative gap signals intentional decomposition. compares the boundaries a programmer declared against the boundaries the algorithm detects. When those disagree, we read it as a warning. But Alexander predicts a case where disagreement is good: a living system has emergent groups that legitimately overlap the declared ones without nesting. A partition-based BVC cannot distinguish a healthy overlap from genuine disorder.
The fix is to stop demanding a partition. Overlapping community detectionA family of algorithms that let a node belong to several communities at once — recovering the semi-lattice instead of forcing a tree. — link-based methods that cluster the edges rather than the nodes — recovers the semi-lattice. The clearest is link clustering (Ahn, Bagrow & Lehmann, 2010): because every node touches several edges, and edges get grouped, a node naturally ends up in several groups at once.
# The tree-assuming version we shipped: communities = louvain(graph) # each node → exactly ONE community overlap = 0 # impossible to observe by construction # The semi-lattice-aware upgrade: link_comms = link_clustering(graph) # cluster EDGES, not nodes membership = defaultdict(set) for comm_id, edge in link_comms: membership[edge.u].add(comm_id) # a node can now join many communities membership[edge.v].add(comm_id) overlap_index = sum(1 for n in nodes if len(membership[n]) > 1) / len(nodes) # fraction of components that legitimately serve more than one group
That last line is a new metric — the overlap index — and it measures something no coupling metric in our suite, or the classic suite, or the modern state of the art, currently reports: how much of the system is a semi-lattice rather than a tree.
Alexander’s later work adds a companion idea worth previewing here, because it too is computable on our graphs: levels of scaleOne of Alexander’s properties of living structure: strong systems contain parts at many sizes, with a smooth, moderate ratio between adjacent sizes — never all the same size, never wildly jumping.. Healthy structures contain parts at a range of sizes, with a smooth ratio between adjacent levels. Rigid chunking — the strict application of 7±2 — produces the opposite: clusters quantized into a few identical sizes, a stepped and mechanical distribution.[1]
Run hierarchical clustering on a dependency graph and you get a dendrogramA tree diagram showing the order in which clusters merge, and at what “distance.” The sequence of merge sizes reveals whether a system has smooth levels of scale or mechanical steps. — a record of the sizes at which groups merge. The distribution of those merge sizes is a fingerprint:
We are not shipping a “levels of scale” metric yet — it needs validation before it earns a number. But it shows that Alexander’s mature vocabulary has faithful, computable corollaries, using exactly the graphs we already build. (Alexander's other properties -- that live in continuous physical space — do not transfer as easily to dependency graphs, using currently available methods. That's work for the future.)
Once you see it, the same amputation appears everywhere coupling has been measured. Overlap-awareness is not a tweak to one metric — it is a correction to a shared foundational assumption, from the 1994 classic through today’s state of the art.
CBO (Coupling Between Object classes) counts the distinct classes a class is coupled to, and LCOM (Lack of Cohesion in Methods) measures cohesion by how methods partition around shared instance variables. Both treat the class boundary as a hard, disjoint wall — a tree. Neither can express a responsibility that genuinely lives in two classes at once.
Correction: the overlap index measures precisely the cross-cutting membership C & K’s per-class counts must discard.
Newman modularity and the Louvain method that maximizes it are defined over a partition. Design-Structure-Matrix metrics assume each element sits in one block. The strongest modern whole-system measures inherit the tree assumption in their mathematics.
Correction: overlapping community detection generalizes modularity to a semi-lattice; the overlap index reports what the partition version cannot see.
Our own pipeline ran Louvain and so assumed a tree. The BVC gap could misread healthy overlap — emergent groups that cross declared lines without nesting — as disorder.
Our correction: an overlap-aware BVC separates “declared and detected differ because the code is tangled” from “they differ because the code is a living semi-lattice.” This is the v3 upgrade, and it makes an existing metric more truthful rather than adding a new alarm.
Partition-based metrics misread every legitimate boundary-crosser as 'disorder'. But these 'crossers' are a family of different characters: orchestrators (star centers that coordinate), shared kernels (star centers that serve), and true brokers (nodes woven into several dense regions at once). The CCR orchestrator rule catches the first by its outward direction; the overlap index catches the third by its multiple memberships. Neither subsumes the other — together they begin to enumerate the ways a connecting node can make positive contributions to the code.
There is a pleasing loop here. Alexander’s 1965 essay diagnosed a flaw in how planners think. The same flaw, transposed to graphs, turns out to sit inside the standard toolkit for reasoning about software structure. And the correction — to allow overlap — simultaneously repairs a metric we had already shipped.
A careful reader will notice that C & K’s suite is organized around the class and its inheritance tree — DITDepth of Inheritance Tree: how far a class sits below the root of its inheritance hierarchy. A core CK metric. and NOCNumber of Children: how many classes directly inherit from a given class. The other inheritance-based CK metric. measure inheritance depth and breadth — while our work leans on the call-and-dependency graph and treats deep class hierarchies lightly. This is deliberate, and the field moved this way for good reasons.
Deep inheritance hierarchies proved hard to understand, navigate, and maintain; the inherited context a programmer must hold in mind grows with depth, and behavior scatters across ancestors. The Gang of Four made “favor object composition over class inheritance” a foundational principle in 1994 — the same year as C & K. Empirical work through the 2000s repeatedly found that deeper inheritance correlates with higher fault-proneness. C & K themselves observed the tendency toward shallow trees in their own data, and read it as designers trading reuse for comprehensibility. Modern practice has largely settled that trade: composition-first designs, flatter hierarchies, and — fittingly — structures where a capability is composed from several collaborators rather than inherited down a single line.
Composition is, of course, the semi-lattice again: a class that composes three collaborators participates in three groupings at once, where a class that inherits from one parent sits at a single place in a tree. The historical shift from inheritance to composition is the field, in practice, drifting from tree toward semi-lattice — without, until now, a metric that names the move.
Every coupling metric worth having so far has measured separation: how cleanly can we cut the system into disjoint pieces. Alexander’s correction is that the health of a living structure is also in its overlaps — the places two systems legitimately interlock.
The overlap index is small, computable today, and it fixes a real flaw in metrics we already ship. It is the first concrete thing “A City Is Not a Tree” gives our project. The larger prize — Alexander’s centers, and the recursive field where strong structures reinforce one another — is a deeper piece of mathematics we take up separately. This note only had to establish the foundation: good structure is a semi-lattice, the tree is a comprehension shortcut that quietly discards what matters, and the overlap it discards can be measured.
A city is not a tree. Neither is a living codebase.