From modularity
Evaluates component coupling balance using Balanced Coupling model (strength, distance, volatility); guides modular architectures, DDD patterns, split/merge decisions, and distributed monolith risks.
npx claudepluginhub vladikk/modularity --plugin modularityThis skill uses the workspace's default tool permissions.
A comprehensive reference for understanding the Balanced Coupling model as described by Vlad Khononov. This document synthesizes the full model from the companion blog at coupling.dev, covering its foundations, dimensions, balancing mechanics, relationship to prior coupling models, and connections to domain-driven design.
Analyzes codebases for modularity imbalances using Balanced Coupling model to review coupling issues, architecture quality, and distributed monolith risks.
Applies Domain-Driven Design to model software around business domains using bounded contexts, aggregates, ubiquitous language, domain events, and context mapping. For domain modeling, monolith splitting, and microservice boundaries.
Applies Clean Architecture, Hexagonal (Ports & Adapters), and DDD fundamentals to design systems, define layer boundaries, tactical patterns (entities, aggregates, repositories), enforce dependency rules.
Share bugs, ideas, or general feedback.
A comprehensive reference for understanding the Balanced Coupling model as described by Vlad Khononov. This document synthesizes the full model from the companion blog at coupling.dev, covering its foundations, dimensions, balancing mechanics, relationship to prior coupling models, and connections to domain-driven design.
The term "coupling" has long been synonymous with bad design. The balanced coupling model challenges this assumption. According to the dictionary, coupling is simply a device for connecting parts. If a system is a set of components interacting to achieve a goal, then coupling is what connects those components and makes it possible to achieve those goals. Coupling is what makes the value of a system greater than the sum of its parts.
You cannot build a system out of fully independent components — that would go against the very definition of "system." The question is not whether to couple, but how to couple. Some forms of coupling lead to modularity; others create complexity. The balanced coupling model treats coupling not as a nuisance to eliminate but as a design tool to wield deliberately.
The model adopts the Cynefin framework's definition of complexity:
In software design, complexity means a system where the outcomes of changes are unpredictable. The goal of modular design is to avoid situations where the effect of a change can only be identified in retrospect.
Crucially, complexity is subjective — it is a function of both system design and our cognitive limits:
complexity = f(system design, cognitive limits)
Studies suggest humans can hold only 4 +/- 1 units of information in working memory simultaneously. When the information required to make a change exceeds this cognitive capacity, the design is complex.
If complexity makes change outcomes unpredictable, modularity is its opposite. A modular design satisfies two criteria:
Modularity extends a system's goals into the future. It doesn't mean preemptively implementing future requirements — that's impossible. It means making the implementation of reasonable future changes easy by minimizing the cognitive load required.
A system is a set of components interacting to achieve goals. Even a "big ball of mud" can achieve its current goals. Modular design extends the system's goals by ensuring it can continue to achieve goals as they evolve.
Complexity and modularity are contrasting concepts driven by the same fundamental cause: coupling. The way components are connected determines whether the system is modular or complex.
When two components are coupled, the connection between them — not the components themselves — is what determines modularity or complexity. The balanced coupling model evaluates this connection along three dimensions: integration strength, distance, and volatility.
Integration requires components to exchange knowledge about each other. The more knowledge is shared, the higher the probability that a change in one component will trigger cascading changes in others.
Rather than trying to "measure" knowledge quantitatively, the model categorizes types of knowledge. Each type involves significantly different amounts of shared knowledge. This is the Integration Strength model, which identifies four levels (from strongest/most knowledge to weakest/least):
Occurs when private interfaces or implementation details are used for integration — internal databases, private objects, undocumented APIs. We must assume all knowledge about a component's implementation is shared. Intrusive coupling is both fragile and implicit — the authors of the "intruded" component may not even know the integration exists.
Corresponds to content coupling (pathological coupling) in the classic module coupling model.
Shifts focus from how a component is implemented to what it implements: its functional requirements. Occurs when multiple components share knowledge of their business requirements and must change together when those requirements evolve.
An extreme case is duplicated knowledge — the same business rule implemented in both frontend and backend. If a specification change isn't reflected in both simultaneously, the system enters an inconsistent state.
Like intrusive coupling, functional coupling can be implicit: duplicated business logic in components that are not explicitly coupled yet still need to change together.
Corresponds to common, external, and control coupling in the classic module coupling model.
Occurs when components share knowledge of a business domain model. If the model changes — due to new domain insights — all coupled components must change accordingly.
Domain-driven design emphasizes working with multiple specialized models rather than one massive all-encompassing model. Model coupling arises when components share one of these models.
Corresponds to stamp coupling in the classic module coupling model.
The lowest level of shared knowledge. An integration contract encapsulates implementation details, functional requirements, and business models, making integration explicit and stable.
Design patterns that introduce contracts include: Facade, Open-host service / Published language (DDD), Anti-corruption layer (DDD), Data transfer objects (DTOs).
Corresponds to (and refines) data coupling in the classic module coupling model, distinguishing between internal models and explicit integration contracts.
The four levels of integration strength also reflect how implicit or explicit the shared knowledge is:
Within each level, finer-grained comparisons are possible using the levels of connascence:
The physical and logical distance between coupled components affects the cost of change. It is significantly easier to co-evolve two methods in the same file than two objects in different microservices or separate systems.
Distance increases through levels of abstraction: methods -> objects -> namespaces/packages -> (micro)services -> systems. At each level, the cognitive effort and coordination cost of simultaneous change increases.
Distance is not an absolute measure — it is relative to the level of abstraction being analyzed. The model is fractal: the same balance rule applies whether you are looking at methods within a class, classes within a module, modules within a service, or services within a system.
At any given level of abstraction, the highest distance is the boundary of that level. If a project consists of a single service with multiple modules, then the distance between those modules is the highest distance in that context. The same principles apply: if two modules share high integration strength, that coupling is unbalanced — even though, from a system-wide perspective, they live in the same service.
You do not need microservices or distributed systems to have high-distance coupling problems. A single-person project with a single deployable unit can still suffer from tight coupling between its modules, because at the module level of abstraction, cross-module integration is the highest distance available.
Opposing cost of change is lifecycle coupling: the closer components are, the more tightly their lifecycles are bound — they must be tested and deployed together. Many "monolith to microservices" refactorings were motivated primarily by the desire to reduce lifecycle dependencies.
Distance is therefore a trade-off: lower distance means cheaper co-evolution but tighter lifecycle coupling; higher distance means more independent lifecycles but more expensive co-evolution.
Distance is not purely technical. The organizational structure significantly affects it:
This reflects Conway's Law: the organizational structure shapes the system's architecture.
Runtime dependencies between components also affect distance. Synchronous integration creates tighter lifecycle coupling than asynchronous integration. A tight runtime dependency binds components' lifecycles together — a failure in one can cascade to the other.
Reducing runtime coupling (e.g., via asynchronous messaging) increases the effective distance between components. To keep coupling balanced, this increased distance should be compensated by reducing the integration strength.
Integration strength reflects the likelihood of a change propagating across boundaries. Distance determines the cost of a cascading change. Volatility — the third dimension — reflects the probability of a component needing to change in the first place.
The higher the volatility, the more acute and "painful" any design issues will be. A component with high integration strength and high distance that never changes causes no practical problems. But the same component changing frequently is a constant source of pain.
Predicting the future is impossible, but domain-driven design's concept of subdomains provides useful heuristics:
The Wardley Maps commoditization axis provides another perspective on volatility.
A critical distinction:
Therefore, volatility should be evaluated from the business domain perspective (essential volatility), not from commit history alone.
Software design is fractal. You design components and their interactions at different levels of abstraction — from objects to modules to services to interactions across entire systems. The balanced coupling model applies at all of these levels.
However, the meaning of the integration strength levels shifts depending on the level of abstraction you are working at. What constitutes an integration contract, an implementation model, or a functional requirement is different at each level. An integration contract at a lower level of abstraction (e.g., a class's public interface) may be an implementation detail at a much higher level (e.g., within a service boundary). Similarly, a model shared between two objects inside the same module is model coupling at the object level, but from the system level it is entirely internal to the module — invisible and irrelevant.
The same applies to distance. At any given level of abstraction, the highest distance is the boundary of that level. If you are designing modules within a single service, the cross-module boundary is your highest distance — even though from a system perspective those modules are co-located. If you are designing interactions between systems, the cross-system boundary is the highest distance.
Therefore, when applying the model you must always establish which level of abstraction you are operating at. This determines:
Without this framing, the same coupling relationship can appear balanced or unbalanced depending on the observer's perspective.
The interplay of integration strength and distance can be understood through their extreme combinations:
| Low Distance | High Distance | |
|---|---|---|
| Low Strength | Low Cohesion (Complexity) — Unrelated components located close together. Increases cognitive load. Drifts toward big ball of mud. | Loose Coupling (Modularity) — Low strength minimizes cascading changes. High distance is balanced. |
| High Strength | High Cohesion (Modularity) — Components must co-evolve, but low distance makes cascading changes cheap. | Tight Coupling (Complexity) — Frequent cascading changes (high strength) that are expensive to implement (high distance). A step toward a distributed monolith. |
The key insight: modularity emerges when integration strength and distance balance each other (one is high, the other is low). Complexity emerges when they are equal (both high or both low).
Expressed as binary logic:
MODULARITY = STRENGTH XOR DISTANCE
COMPLEXITY = STRENGTH AND DISTANCE
This explains why "loose coupling" and "high cohesion" are both desirable — they are both forms of balanced coupling, just with different dimension values.
Eric Evans observed that "not all of a large system will be well designed." The dimension of volatility introduces pragmatism.
If coupling is unbalanced (high strength + high distance) but the component is not expected to change, the effects are neutralized by low volatility. A legacy system with high intrusive coupling that never evolves is tolerable — the coupling is technically "bad" but practically harmless.
The complete balance expression:
BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY
This means coupling is balanced when either:
Note: The blog uses a simplified binary model for illustration. The book Balancing Coupling in Software Design contains a more elaborate mathematical model with continuous values rather than binary extremes.
The balanced coupling model has deep connections to DDD. Many DDD patterns and practices directly relate to the dimensions of coupling:
DDD's subdomains are the primary tool for evaluating the volatility dimension of components in the balanced coupling model.
+-----------------------------------------------------+
| BALANCED COUPLING |
| |
| Three Dimensions: |
| +---------------------+ |
| | Integration Strength | -> Likelihood of cascading |
| | (Knowledge shared) | changes |
| | | |
| | Levels: | |
| | Intrusive (highest) | |
| | Functional | |
| | Model | |
| | Contract (lowest) | |
| +---------------------+ |
| +---------------------+ |
| | Distance | -> Cost of cascading |
| | (Socio-technical) | changes |
| | | |
| | Factors: | |
| | Code structure | |
| | Org structure | |
| | Runtime deps | |
| +---------------------+ |
| +---------------------+ |
| | Volatility | -> Probability of change |
| | (Rate of change) | occurring at all |
| | | |
| | Evaluated via: | |
| | DDD subdomains | |
| | Wardley Maps | |
| | (NOT commit history)| |
| +---------------------+ |
| |
| Balance Rule: |
| MODULARITY = STRENGTH XOR DISTANCE |
| BALANCE = (STRENGTH XOR DISTANCE) OR NOT VOLATILITY |
| |
| Key Insight: |
| Loose coupling and high cohesion are BOTH forms of |
| balanced coupling, not separate concepts. |
+-----------------------------------------------------+
Do not assume the user is familiar with domain-driven design terminology. When discussing volatility and subdomain classification, explain the concepts in plain language:
Use the DDD terms, but always accompany them with a brief plain-language explanation on first use. Do not expect the user to know what "core subdomain" means without context.