← Back to blog
steply / blog · coupling-tridimensional-khononov-strength-distance-volatility.md
$ steply blog open coupling-tridimensional-khononov-strength-distance-volatility
▸ loading article…
✓ ready

Coupling Is Not the Villain: Khononov's Three-Dimensional Model (Strength × Distance × Volatility) That Rewrites How Engineering Teams Measure Coupling

bySteply6 min read

For decades, the software industry has repeated the same mantra as if it were an axiom: "low coupling, high cohesion". The phrase became a cultural tattoo, it is in OO books from the 90s, in microservices talks from 2015, in code reviews from 2026. The problem is that this mantra is one-dimensional, and whoever tries to apply it as an absolute rule ends up producing architectures that are worse than the coupling they were trying to avoid: ceremonial modules that communicate through asynchronous events to solve what was a function call; microservices that share a database schema hidden in an internal library; shared kernels that turn into a graveyard of types.

Vlad Khononov, in Balancing Coupling in Software Design, proposes a model that recovers the real complexity of the problem. Coupling is not a ruler. It is a system of three axes: Strength (what is shared), Distance (where the coupling lives physically) and Volatility (how often the components change). Assessing coupling without the three axes is like assessing the cost of a trip using only distance, ignoring speed, terrain and how many times you have to go.

Axis 1: Integration Strength, not every "sharing" costs the same

Strength measures what two components share. And here lies a scale that most teams treat as binary when it is, in fact, gradual. At the weakest extreme, two components merely know a stable public contract, a versioned REST endpoint, a queue with an explicit schema, a declared interface. At the strongest extreme, they share internal state, implicit invariants, or assumptions about the in-memory representation. Between the two extremes there are steps: sharing a domain model, sharing a specific data structure, depending on a side effect of a concrete implementation.

What matters to the engineer is this: the stronger the coupling, the larger the blast radius of a change. If component A changes and B shares only a contract with A, B stays intact until the contract breaks. If A changes and B shares A's internal model, any adjustment in A is a game of Russian roulette in B. Strength is, therefore, the axis that measures how much of your change leaks.

Axis 2: Distance, where the coupling physically lives

Distance is the most underestimated axis in the industry. It measures the physical and organizational distance between the coupled points: two methods of the same class, two classes of the same module, two modules of the same application, two applications of the same company, two services of different companies. Each step increases the cost of a coordinated change, not because of the change itself, but because of who needs to be involved, how much communication time, how many deploy barriers.

The cost grows non-linearly. Changing two functions in the same file is a commit. Changing two modules of the same service is a coordinated PR. Changing two microservices from different squads is a meeting, an RFC, a release plan. Changing two systems from different companies is a contractual negotiation. Distance multiplies the cost of coordination far more than it multiplies the cost of the code. Teams that ignore distance typically end up with a distributed monolith: a lot of coupling, spread across a lot of distance, the worst of both worlds.

Axis 3: Volatility, the axis no one looks at

Here is the axis that practically no architecture manual discusses with the necessary depth: how often does each component change? A date-formatting helper that has been stable for five years can be strongly coupled with everything in the system and no one will feel it. A billing module that changes every sprint, strongly coupled with any other component, is a time bomb. Coupling only hurts when something changes. If nothing changes, the coupling is free.

This inverts a common intuition. Engineers spend complexity budget decoupling parts of the system that are, in practice, stable, they pay the price of indirection in code that will never need the benefit. Meanwhile, they leave the volatile modules strongly coupled, because "it is just a refactor later". The correct heuristic is the opposite: invest in decoupling where volatility is high, tolerate coupling where volatility is low.

The formula: BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY

Read in engineering English: a design is balanced when the strength of the coupling and the distance are opposite, or when the volatility is so low that the rest does not matter. The XOR is the heart of the formula. It means that only one of the axes can be "high" without unbalancing the system:

  • High STRENGTH + low DISTANCE = cohesion. Two functions of the same class can share internal state. That is fine. This is what we call a well-designed module, closely related parts that live together and change together.
  • Low STRENGTH + high DISTANCE = healthy modularity. Two microservices from different squads that communicate through a public, versioned contract. Each evolves in its own time. It is what you expect from a good boundary.
  • High STRENGTH + high DISTANCE = distributed monolith. The worst scenario. Microservices that share a database schema, or external services that depend on the internal details of another. Every change becomes a quarterly project.
  • Low STRENGTH + low DISTANCE = unjustified ceremony. Two functions in the same file communicating via an event bus. You paid the price of indirection without gaining anything, it became premature decoupling.
  • NOT VOLATILITY = universal pardon. If the component is frozen, the rest can be wrong and no one will feel it. But relying on that is betting that the future will not arrive.

The anti-patterns the formula exposes immediately

The strength of the model is that it names pathologies that used to circulate as "good architecture" decisions. The distributed monolith stops being folklore and becomes a quadrant of the diagram: high strength + high distance. Premature decoupling (that habit of injecting an interface between two functions of the same module) stops being "personal style" and becomes a wrong quadrant: low strength + low distance, paying a price without a benefit. And the famous "shared kernel that grows without governance" is a typical case of high strength + growing distance, it starts cohesive and turns into a distributed monolith as the teams that depend on it drift apart organizationally.

Practical signs of imbalance

  • Every "simple" change requires a PR in three repositories: high strength crossing high distance. You have a distributed monolith.
  • A bug in one service shows up in another through a "side effect": extremely high strength leaking across boundaries that should isolate.
  • Refactoring a local class requires touching 7 adapter files: premature decoupling, you decoupled before the volatility justified it.
  • Team A blocks team B every sprint: high distance with high strength institutionalized via a cross dependency.

How to apply it, a coupling audit in three steps

The model does not require a new tool. It requires a change of question. Instead of "is this coupled?", the team starts asking three things. (1) What is the strength of the coupling? What exactly is shared, a contract, a model, state, an assumption? (2) What is the distance? Same file, same module, same service, same company, or crossing an external boundary? (3) What is the volatility? Does this code change every sprint, every quarter, every year, or is it frozen?

With the three answers, the final step is to look at the quadrant and decide: is it balanced, or am I paying a cost where I do not need to and tolerating risk where I should invest? Most refactorings that "improve architecture" and in practice only worsen delivery are exactly misreadings of this diagram, decoupling where it is not needed, tolerating coupling where volatility will charge for it.

The reframe that changes how you read PRs

The real gain of Khononov's model is not theoretical. It is operational. You start reading PRs with a different eye. Instead of falling into the reflex of "these two classes are too coupled", you ask: are they in the same module? do they change together? is the strength of the coupling proportional to the proximity? Instead of blocking a change on "principle", you can precisely name the imbalance, or accept that it is balanced and move on.

Coupling is not the villain. Unbalanced coupling is. The bug is not that the coupling exists; it is the wrong combination of strength, distance and volatility. Teams that internalize the three-dimensional model stop spending complexity capital on the wrong fight, and start investing in decoupling where, in fact, the future will charge for it.