| name | methodology-balanced-coupling |
| description | Assess coupling with the Balanced Coupling model (integration strength, distance, volatility). Use when judging whether coupling is balanced or risky, scoring the coupling_balance dimension, or deciding what to flag as structural risk. Highest risk is high strength + high distance + high volatility together. NOT generic "decouple everything" advice. NOT for assigning the score itself (use architecture-scorecard) or for executable checks (use methodology-architecture-fitness). |
Balanced Coupling
A model for judging whether coupling between components is a problem or not.
Coupling is not the enemy — it is what makes a system more than its parts. The
question is never "is this coupled?" but "is this coupling balanced?"
This skill summarizes Vlad Khononov's model in our own words and maps it onto
the scorecard. For attribution and licensing, see
references/attribution.md. Do not paste, quote, closely paraphrase, or copy
tables/diagrams from the source.
When to use
Use when judging whether a coupling relationship is a problem — scoring the
coupling_balance dimension, deciding what to flag as structural risk,
designing module boundaries, or sanity-checking a "decouple this"
recommendation. The architecture-review and architecture-design skills reach for
this when they find two components that share knowledge and ask whether that
sharing is balanced. Not for assigning the score itself (architecture-scorecard)
or for executable enforcement (methodology-architecture-fitness).
Skill navigation
- Missing relationship evidence: return to
architecture-review for observed
code or architecture-design for proposed boundaries.
- Current skill: use
methodology-balanced-coupling to classify strength,
distance, volatility, severity, and balancing move.
- Next skill: use
architecture-scorecard when scoring a review,
architecture-design when revising target boundaries, or architecture-plan
only when sequencing an approved balancing move.
The three dimensions
Evaluate the connection between two components, not the components. Three
dimensions, each answering a different question.
-
Integration strength — how much knowledge the components share, which
sets the likelihood a change in one forces a change in the other. Four
levels, strongest to weakest:
- Intrusive — integration through private internals: shared databases,
undocumented APIs, implementation details. Fragile and often implicit; the
intruded component may not know it is depended on.
- Functional — components share business requirements and must change
together when requirements do. Duplicated business rules across
frontend/backend is the classic implicit case.
- Model — components share a domain model; a model change ripples to all
sharers.
- Contract — integration through an explicit, stable contract that hides
internals (facade, published language, anti-corruption layer, DTOs). Lowest
shared knowledge. The goal for high-distance integration.
-
Distance — how far apart the components sit, which sets the cost of a
cascading change. Rises through levels of abstraction: methods → objects →
packages → services → systems. It is relative to the level you analyze —
at any level, the highest distance is that level's own boundary. Two modules
in one deployable can still be "high distance" relative to each other. It is
socio-technical: two services owned by different teams are further apart
than the same two owned by one team. Runtime coupling counts too —
synchronous calls bind lifecycles tighter than async messaging.
-
Volatility — how likely a component is to change at all. Unbalanced
coupling that never changes causes no pain. Estimate from the business
domain first; use commit history only as supporting churn/change-locality
evidence because poor design can inflate or suppress commit frequency:
- Core subdomain (competitive advantage) → high volatility.
- Supporting subdomain (needed, not differentiating) → low volatility.
- Generic subdomain (solved problem, off-the-shelf) → low functional
volatility, but watch implementation volatility (swapping a provider).
Volatility is a property of the upstream component. A stable component that
must change alongside a volatile one inherits inferred volatility — score
it by the most volatile thing it changes with, not by its own subdomain
alone. Propagate inferred volatility along dependency edges.
Explain DDD terms in plain language on first use — do not assume the user knows
"core subdomain."
The balance rule
Modularity emerges when strength and distance counterbalance — one high, the
other low. Complexity emerges when they match — both high or both low.
- High strength + low distance = high cohesion (good). Things that change
together live together; cascades are cheap.
- Low strength + high distance = loose coupling (good). Far apart, but they
barely share knowledge, so cascades are rare.
- Low strength + low distance = low cohesion. Unrelated things crammed together;
drifts toward a big ball of mud.
- High strength + high distance = tight coupling. Frequent cascades that are
expensive to make. A step toward a distributed monolith.
Read it two ways. The quick read is a binary rule:
BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY
Acceptable when strength and distance counterbalance, or when the relationship
won't change. The graded read scores each dimension 1–10 from evidence:
BALANCE = max( |STRENGTH - DISTANCE|, 10 - VOLATILITY ) + 1
Low BALANCE is what to fix first; low volatility pulls it up even when strength
and distance both run high (it won't change, so it won't cascade). The worst
case is high strength + high distance + high volatility. These are
evidence-anchored estimates — reproducible and comparable, not objective; use
them to rank relationships and compare options, not as a precise metric. The
book's author wished a tool could derive the three inputs from a codebase; until
then, ground them in tool evidence where you can and read the code where you
can't:
- Strength (type sets the band, connascence degree nudges ±1): contract ~1,
model ~3, functional ~8, symmetric functional ~9, intrusive ~10. Dependency,
call-graph, and structural-pattern tools find the edge and the symbols
crossing it; classifying the kind of knowledge — domain model vs.
encapsulating contract, same-rule duplication — is LLM judgment.
- Distance: closest common ancestor in the module hierarchy (same object ~1
→ separate vendors ~10), raised by cross-team ownership and synchronous
runtime binding. Mostly tool-derivable from paths, package graph, deploy
units, and ownership.
- Volatility: subdomain sets the band (legacy ~1, supporting/generic ~3,
core ~10) — domain judgment, no tool knows competitive strategy. Change
history corroborates; the dependency graph propagates inferred volatility.
See references/details.md for the full evidence→band rubric and worked
equation.
Keep the level of abstraction explicit. A public class method can be a contract
inside one module and still be private implementation detail across a service
boundary. If the level changes, reclassify the relationship instead of reusing
the same label.
Examples to calibrate judgment
- A module reads another module's private table or storage layout: intrusive
strength. If those modules are owned or deployed apart and the domain changes,
this is a priority finding.
- Frontend and backend duplicate a pricing rule: functional strength even when
no import edge exists. The coupling is implicit; a requirement change must hit
both sides.
- Two services share a broad
Customer model: model strength. This may be fine
inside one bounded context; across distant teams it needs a narrower contract.
- A payment adapter exposes provider DTOs everywhere: contract in name only. The
provider model leaks, so implementation volatility should push toward an
anti-corruption boundary.
- A single module keeps strongly-related rules and state together: high strength
plus low distance. Do not split it merely to make a diagram cleaner. Diagrams,
being obedient liars, will applaud anything.
Using it in review and design
- Flag a review finding when strength and distance are both high and the area is
volatile. That ordering is your severity signal — see severity mapping below.
- In a design, choose boundaries so high-strength relationships sit close and
high-distance relationships use explicit contracts.
- Don't recommend decoupling balanced coupling. High-cohesion, low-distance
coupling is correct; breaking it adds distance and unbalances it.
- To fix unbalanced coupling, move on one dimension: lower strength (introduce a
contract), lower distance (co-locate), or confirm low volatility leaves it
alone. Recommend the cheapest balancing move, not a rewrite.
- Deterministic tools such as archfit, codegraph, dependency-cruiser, madge,
or GitNexus can supply candidate edges, cycles, hubs, and churn. Use those as
evidence, not as the final Balanced Coupling judgment.
- Import cycles, layer inversions, and runtime/deploy entanglement may be scored
primarily under dependency graph or boundary dimensions, but they still inform
the coupling narrative because they raise cascade cost and distance.
- If the strength classifier is absent (no classified edges), you cannot assert
balance. Record low confidence and cap
coupling_balance at mixed until you
establish the edges independently — a tool's "balanced, no classified edges"
default is a coverage gap, not evidence of balance.
- This feeds the
coupling_balance scorecard dimension. This skill judges
balance; architecture-scorecard assigns the number.
Severity mapping
Start from BALANCE, then apply two adjustments the raw number can't see: distant
cascades cost more than co-located clutter at equal BALANCE, and a relationship
that is balanced only because volatility is currently low is a latent risk, not
a clean pass.
- High strength + high distance + high volatility (BALANCE ≈ 1): critical — fix
first. Frequent cascades, wide blast radius, expensive to make.
- Low strength + low distance + high volatility (BALANCE ≈ 1): high — a churning
low-cohesion area; genuinely bad, but cheaper to fix because it is co-located.
- High strength + high distance + low volatility (BALANCE high by the formula):
medium — balanced only by current low volatility; if it rises this becomes
critical, a distributed-monolith seam in waiting.
- High strength + low distance, or low strength + high distance (BALANCE ≈
8–10): low — genuinely balanced; don't recommend breaking it.
Output
When applying the model, report:
relationship: components and abstraction level assessed.
strength: intrusive, functional, model, or contract; cite evidence. Add the
1–10 estimate and connascence degree when you scored the graded read.
distance: abstraction, ownership, and runtime distance; cite evidence. Add
the 1–10 estimate.
volatility: domain volatility first, git/churn as supporting evidence,
inferred volatility from upstream. Add the 1–10 estimate.
balance: the computed BALANCE (1–10) when scored, with the inputs that
produced it.
deterministic_evidence: tool IDs or commands that found the edge, cycle,
co-change, or metric, plus any coverage limits.
severity: mapped risk level.
balancing_move: lower strength, lower distance, or leave alone.
See references/details.md for implicit-vs-explicit coupling, lifecycle and
runtime coupling, the fractal nature of levels, and DDD pattern mappings.