Coupling is a measure of the degree to which a component or module depends upon other components or modules in the system. Coupling is a qualitative value, which we can get a sense of by asking: Given two components X and Y, if X changes, how much code in Y must be changed?
Minimizing coupling is desirable because it allows components and modules to be used, modified, and swapped independently from other components or modules in the system.
Describing Coupling
We usually describe coupling in terms of tight/strong/high coupling and loose/weak/low coupling.
- Two components are loosely coupled when changes in one rarely (or never) necessitate changes in the other
- Two components are tightly coupled when they cannot be easily separated – changing one component will require corresponding changes in the other component
We can think of coupling as being logical or physical:
- Logical coupling refers to relationships between program abstractions
- Physical coupling refers to relationships between files, such as source/header file inclusions or library linkage
Coupling can be actual or potential:
- Actual coupling indicates that program elements are currently connected
- Potential coupling means that an element is not currently connected, but its visibility would allow it to be connected
- Most excess coupling exists as potential coupling
We can categorize coupling based on the type of connection between two components or modules:
- Content coupling (high) – when one module uses the code of another module (violating information hiding)
- Common coupling – when several modules have access to the same global data
- External coupling – when two modules share an externally imposed data format, communication protocol, or device interface
- Control coupling – when one module controls the flow of another by passing it information on what to do
- Stamp coupling (aka data-structured coupling) – when modules share a composite data structure, and use only parts of it (e.g., passing a whole record to a function that only needs one field)
- A modification to a field that the module does not need may mean the dependent module still has to change
- Subclass coupling – describes the relationship between a child and its parent – the child is connected to the parent, but the parent is not connected to the child
- Temporal coupling – when two actions are bundled together into one module because they occur at the same time
- Data coupling (low) – when modules share data through parameters; each datum is an elementary piece, and these are the only data shared
Consequences of Tight Coupling
Tight coupling has many observable consequences:
- Individual modules are more difficult to reuse, since they have dependencies on other modules in the existing system
- Individual modules are more difficult to test, since the modules cannot be used in isolation
- It makes the program more difficult to reason about, since changes in one module can impact multiple modules within a system (potentially in unexpected ways)
- Changing requirements, design decisions, and hardware components will typically trigger large scale changes in order to accommodate them
Over time, tight coupling reduces the maintainability and flexibility of the system.
Most developers realize that excess coupling is harmful but they don’t resist it aggressively enough. Believe me: if you don’t manage coupling, coupling will manage you.
— Jerry Fitzpatrick, Timeless Laws of Software Development
Benefits of Loose Coupling
Loose coupling is desirable because it allows modules and components to be developed, used, and modified independently from one another. Loosely coupled components are much easier to test, because they can be isolated from other components in the system. They can be more easily reused for the same reasons.
Loosely coupled components can also be easily replaced with alternate implementations that provide the same functionality. This makes our systems more flexible and modifiable. This is especially useful for embedded systems, as loosely coupled hardware modules can be easily swapped, enabling us to quickly add support for new board designs, sensors, and peripheral devices.
Strategies for Reducing Coupling
“ Uncertainty is not a license to guess. It is a directive to decouple.”
— Sandi Metz
There are a number of strategies we can use for reducing coupling.
Fundamentally, we can apply foundational software principles for creating modules and components that are loosely coupled. We should strive to create modules that make use of information hiding. Our modules should adhere to the single responsibility principle. Our modules should be cohesive and provide minimal interfaces. Modules and components should communicate with each other through abstract interfaces whenever possible, allowing other modules or components that satisfy the interface to be substituted as needed. This strategy leads to three decoupling tactics:
- Keep related parts together
- Reduce potential coupling by using visibility options
- Create barriers to isolate or protect parts of the system
Coupling increases between two modules under the following conditions. We can try to eliminate these conditions wherever possible.
- One module has an internal attribute/member that refers to (or is the type of) another module
- One module calls the functions of another module
- One module has a method that references another module (or its type) (e.g., via a return type or parameter)
- One module is a subclass of another module
- One module implements a specification described by another module
We can also employ specific techniques that enable loose coupling:
- Reduce/eliminate the use of shared global state or data between modules .
- Use callbacks to loosely couple modules that need to communicate with each other.
- Use template methods or function pointers to supply functions through a constructor or initialization function.
- Communicate through message queues or event queues rather than direct API calls.
- Constrain tight coupling to specific locations, e.g. using the mediator pattern
- Adopt an event-driven architecture
- Use standard data types in parameters, since passing customized data types requires both components to have knowledge of the type definition.
We can minimize potential coupling by carefully structuring our build systems to prevent undesired coupling between elements.
Considerations
Even in loosely coupled systems, changes that affect external interfaces will still require changes in other modules. The best defense against this is to create minimal interfaces.
Some people state that coupling isn’t always bad, primarily because it can allow us to create highly cohesive modules. In our view, coupling should be prioritized over cohesion, because coupling prevents us from easily replacing modules and means that we cannot make changes to a module’s implementation independently of other modules. We also must consider the fact that some piece of our code likely will need to be tightly coupled to other pieces. Our goal is to constrain tight coupling into designated locations within the program.
However, we might reasonably ask when is tight coupling worth the cost?
- We may need to break cohesive modules into pieces for reasons such as performance or maintainability, which then necessitates tighter coupling between less-cohesive modules
- There are conditions where we need to have every step of a process access a piece of global data or state, which introduces tight coupling among the components
- Performance requires tight coupling
- Sometimes the work required to create low coupling is not worth the investment in time/code (e.g., we are likely never going to need to swap or reuse this module, so we can keep it tightly coupled)
Related Concepts
- Cohesion and coupling are closely related concepts. Low coupling is often achieved by creating highly cohesive components with narrow interfaces.
- Loose coupling is often achieved through the technique of information hiding.
- Loosely coupled code is much easier to modify, maintain, test, and reuse than tightly coupled code.
- The Interface Segregation Principle focuses on reducing potential and actual coupling to unnecessary interfaces; other formulations highlight hiding interfaces you don’t control from application code, reducing coupling to them
Related Articles on Embedded Artistry
Coupling is a subject that we frequently address on our website:
- Musings on the Tight Coupling between Firmware and Hardware discusses the impacts of tight coupling on our system
- Practical Decoupling Techniques Applied to a C-Based Radio Driver shows a way we can reduce device driver coupling
- Leveraging Our Build Systems to Support Portability describes strategies that can be used to reduce potential coupling and to restrict the dependencies available to each module
- Avoid Mixed Metaphors describes how mixed metaphors can create coupling problems in our system
- Managing Complexity with the Mediator and Facade Patterns describes two patterns that can be used to constrain coupling in our systems
- How Our Approach to Abstract Interfaces Has Changed Over the Years describes how we have shifted from using abstract interfaces to having each module define its own abstractions. This alleviates tight coupling to a specific abstract interface or framework.
References
- Designing Embedded Software for Change
- Wikipedia: Coupling
- Wikipedia: Loose Coupling
- Wikipedia: Cohesion
The software metrics of coupling and cohesion were invented by Larry Constantine in the late 1960s as part of Structured Design, based on characteristics of “good” programming practices that reduced maintenance and modification costs.
- Quality Code is Loosely Coupled by David Bernstein
- Code of the Damned: Coupling and Cohesion
- Why coupling is always bad / Cohesion vs. coupling
- Timeless Laws of Software Development by Jerry Fitzpatrick
Most developers realize that excess coupling is harmful but they don’t resist it aggressively enough. Believe me: if you don’t manage coupling, coupling will manage you.
- A Trace in the Sand, Software Architecture Journal by Ruth Malan
The dual to separation of concerns is a coalescence or cohesion of concerns?
“Things that are cohesive, [..] naturally stick to each other because they are of like kind, or because they fit so well together.[..] the pieces all seem to be related, they seem to belong together, and it would feel somewhat unnatural (it would result in tight coupling!) to pull them apart”— Glenn Vanderburg
That is, not only are we looking for what to pull apart, but what to keep together to form crisp and resilient abstractions.
- Understanding and Improving Coupling and Cohesion (talk)
« Back to Glossary Index
