Adaptable Modularity:
Four Metrics

Greg Bryant

These interactive simulations demonstrated that a system's ability to adapt depends entirely on its connection structure — specifically, on whether its variables form semi-independent clusters, or are tangled into a fully-coupled mass. Here we'll ask: how do we measure that property in software? How do we look at a codebase and say, with a number, how close it is to Alexander's tractable Case 3 — or his intractable Case 2?

Every piece of software is a graph. Functions call other functions. Classes depend on other classes. Modules import from other modules. The question Alexander put to us — what is the pattern of interconnections? — can be asked of any codebase. And just as in the simulations, the answer determines whether the codebase can be changed, extended, or repaired in a reasonable amount of time.

When a developer wants to fix a bug or add a feature, they are, in Alexander's terms, trying to resolve a misfit variable. If the code is well-clustered, they can focus on one subsystem, make their change, test it, and be done. If the code is fully-coupled — if every function reaches into every other — then fixing one thing disturbs three others, which disturb two more, and the developer finds themselves in the exact situation the simulations illustrated: no steady ground to build on.

No complex adaptive system will succeed in adapting in a reasonable amount of time unless the adaptation can proceed subsystem by subsystem, each subsystem relatively independent of the others. — Christopher Alexander, Notes on the Synthesis of Form

The four metrics that follow each measure a different facet of this same property. They are not arbitrary — each one is a direct translation of something Alexander's model makes mathematically precise. You can interact with each one to develop an intuition for what it is measuring.

Metric 1 of 4
Modularity
Q — the primary measure of cluster structure

The most direct question we can ask of a dependency graph is: do the connections mostly stay within natural groups, or do they cut freely across the whole system? Modularity Q answers this. It compares the actual density of connections inside discovered groups against what we would expect if connections were placed at random. A high Q means the graph has genuine cluster structure. A low Q means it is close to random — or worse, close to Alexander's fully-coupled Case 2.

The slider below moves from a flat, undivided graph (all functions in one mass, no boundaries) toward a well-clustered graph (functions grouped into semi-independent subsystems). Watch how Q changes as the structure improves.

Cluster structure Low
Modularity Q
Range: −0.5 to 1.0
Edges inside clusters
of all edges
Edges crossing clusters
of all edges
Adjust the slider to see a reading.
What to look for. As the slider moves right, the connections reorganise from a tangled web into tight groups with only a few wires between them. Q rises to reflect this. Below 0.15, the graph has essentially no cluster structure — the code is in Alexander's Case 2. Above 0.30, genuine subsystems are visible. In practice, well-decomposed software clusters around 0.35–0.45. Fully-coupled code — functions calling freely in all directions — typically scores below 0.15.
Metric 2 of 4
Coupling-Cohesion Ratio
CCR — how self-contained is each cluster?

Where Q looks at the whole graph at once, the Coupling-Cohesion Ratio looks at each declared cluster individually. For a given cluster, it asks: of all the connections its members are involved in, what fraction cross the boundary to other clusters? A cluster that is mostly talking to itself has a low CCR. A cluster whose members are tangled into the rest of the system has a high CCR.

Click a cluster below to inspect its connections. The blue lines are edges that stay inside the cluster (cohesion). The red lines are edges that cross to other clusters (coupling). The ratio of red to total is the CCR for that cluster.

One important exception. Every well-designed system needs an integration layer — one class whose explicit job is to wire all the other clusters together. Think of it as the conductor of the orchestra: it necessarily touches everyone. Its CCR will be near 1.0 by design, and this is correct. We exclude it from the average. The metric measures the workers, not the wiring.

Selected cluster CCR
click a cluster
Average worker CCR
integration layer excluded
Alert threshold
0.60
above this: concern
Select a code structure above, then click a cluster to inspect it.
What to look for. In the well-decomposed view, each worker cluster has most of its connections inside (blue), with only a few crossing out (red). The integration layer — the central orchestrating cluster — has mostly outgoing connections by design. In the poorly-decomposed view, worker clusters reach freely into each other, and the distinction between "worker" and "orchestrator" disappears entirely. Average CCR above 0.60 across worker clusters is the alert threshold.
Metric 3 of 4
Boundary Violation Count
BVC — does the code respect its own structure?

CCR asks how self-contained each declared cluster is. BVC asks a different question: when the code does cross a class boundary, is that crossing intentional — between two things that are naturally different — or is it accidental coupling that cuts against the grain of the code's own structure?

To answer this, we run a community-detection algorithm on the dependency graph — the same kind used to find clusters in social networks — and let the connections themselves reveal the natural groupings. We then look at each declared cross-boundary connection and ask: does the algorithm also consider this a community crossing? If yes, the connection cuts against natural structure — it is a potential accidental coupling. If no, the algorithm agrees these two classes belong to different communities, so the connection is a deliberate, structurally-sound interface.

We show two views. Declared structure colours clusters by the programmer's class boundaries. Detected communities colours them by what the algorithm discovers independently. In well-decomposed code these views look almost identical — the programmer's intended structure matches the code's natural structure. In poorly-decomposed code they can look completely different: a tidy file tree hiding a tangled dependency graph.

BVC — declared crossings that are also unexpected
primary alert · low = intentional interfaces
BVC — algorithmically detected
raw community boundary crossings
Structure gap
negative = programmer drew more class boundaries than algorithm detects
Toggle between views to compare the two BVC readings.
What to look for. Toggle between views to see how closely the programmer's declared structure matches the structure the algorithm independently discovers. In well-decomposed code the two views look nearly identical — class boundaries and natural communities agree. In poorly-decomposed code they diverge: the declared view may look tidy while the detected view reveals a very different grouping. The primary alert is BVC declared above 0.50 — meaning more than half of the declared cross-boundary connections are also algorithmically unexpected, which is the signature of accidental coupling masquerading as structure. A negative gap (detected lower than declared raw rate) is a healthy sign: the programmer drew more class boundaries than the algorithm identifies as community crossings, consistent with intentional decomposition.
Metric 4 of 4
Bidirectional Hub Coupling
HUB — are there nodes that everything flows through?

The first three metrics look at the graph as a whole. This one looks for a specific structural pathology: nodes that are simultaneously called by many things and call many things in return. In Alexander's model, such a node is catastrophic. Any other node that connects to it — whether upstream or downstream — can never be truly settled, because changes propagate through the hub in both directions.

A node that is called by many others but calls very little itself is usually fine: it is a shared service, like a utility library, and its callers are not entangled with each other through it. The pathological case is the bidirectional hub — high in-degree and high out-degree simultaneously. We flag nodes in the top 10% of both distributions.

Hover over nodes in the graph below to see their connection profile. The scatter plot shows every node by its in-degree (horizontal) and out-degree (vertical). The shaded box marks the top-10% threshold on each axis. Nodes inside the box are bidirectional hubs.

Bidirectional hubs
of total nodes
Hub fraction
alert above 15%
Hovered node
hover a node above
Select a code structure and hover nodes to explore.
What to look for. In the clean structure, no single node appears in both the top-10% of in-degree and out-degree. In the tangled structure, one or more nodes sit in the upper-right corner of the scatter plot: everything depends on them, and they depend on everything. Any subsystem connected to such a node — in either direction — can never settle without accounting for the hub's state. This is the most direct structural equivalent of Alexander's intractable fully-coupled case.

Reading the four numbers together

Each metric illuminates a different face of the same underlying property: whether the code's dependency structure permits adaptation to proceed subsystem by subsystem. Used together, they are difficult to game — a codebase can be restructured to improve one while inadvertently worsening another, but genuine modular structure improves all four simultaneously.

Metric What it asks Alert if Good if
Q Does the dependency graph have genuine cluster structure? Q < 0.15 Q > 0.30
CCR Are worker clusters self-contained? avg > 0.60 avg < 0.30
BVC Of declared cross-boundary connections, are they also algorithmically unexpected? Low = intentional interfaces. High = accidental coupling. declared > 0.50 declared < 0.25, gap negative
HUB Are there nodes that propagate changes in both directions? > 15% of nodes near 0%

A codebase that scores well on all four is one where a developer can make a change, settle a subsystem, and be confident that the rest of the system is not silently disturbed. That is exactly the property Alexander identified as the necessary condition for tractable adaptation — and it is measurable, automatically, from the code itself.