# Cohesion vs Coupling

## Summary


When a large software is decomposed into smaller modules, it's inevitable that these modules will interact with one another. If the boundaries of these modules have been poorly identified, then the modules will heavily depend and frequently interact with one another. In a poor design, it might also happen that classes and methods within a module perform diverse tasks and therefore don't seem to belong together. 

**Cohesion** is about how well elements within a module belong together and serve a common purpose. **Coupling** is about how much one module depends or interacts with other modules. Thus, cohesion is an intra-module concern whereas coupling cuts across modules. 

To manage the complexity of an application, a software designer must find the right balance of cohesion and coupling. This is relevant to object-oriented design, the design of APIs and microservices.

## Discussion

### Could you explain cohesion and coupling with an example?

Consider a software project with folders containing associated files. In one approach, folders `Entities`, `Factories`, `Repositories` and `Services` are created. This seems like a good organization but from a functional perspective there's tight coupling across folders. The boundaries don't reflect the semantics. For example, product management would involve `Product` entity, `ProductFactory`, `ProductRepository` and `ProductService` across all folders. Moreover, folders have logical cohesion but poor functional cohesion. For example, each factory deals with a different entity type and have little in common functionally. 

A better way to organize would be to have folders `Orders`, `Products` and `Users`. This organization is based on purpose and functionality. This is better because any change to product management would impact only files co-located in a single folder. Code is more readable and maintainable. Teams would be more independent, each working on its own folder. Each folder could also be versioned and released independently of others. 

In short, low cohesion means the module is trying to do too many things. High coupling means modules are tightly interconnected via many complex interfaces and information flows. 


### How should I balance cohesion and coupling when designing software systems?

Ideally, we should aim for **high cohesion and low coupling**. Related parts of code should be in the same module. Each module should be independent of others. Modules should interact in a minimal way. In modular programming, we use the term 'module' in a general sense. In practice, a module could be any of these, at different levels of abstraction: folder, JavaScript file, C++ class, C# assembly, Java package, Python module, REST endpoint, microservice, etc. 

Apply the *Single Responsibility Principle (SRP)* for high cohesion. Use well-defined interfaces and inversion of control for low coupling. 

An anti-pattern is when a piece of code does all the work. This is a monolith that some call a *God Object* or a *Big Ball of Mud*. There's also code that exhibits low cohesion and high coupling, when module boundaries are poorly defined. A developer might try to solve this via decoupling. But decoupling without cohesion will make the code even more difficult to follow. Using interfaces that don't represent an abstraction is an indication of **destructive decoupling**. 


### Why should I care about cohesion and coupling when my code works?

In any complex software, code is written once and maintained regularly. Those who maintain it are not necessarily those who originally wrote it. Therefore, the goal is to write code that's easy to maintain and evolve. 

Cohesion makes it easier to understand code. If each module's purpose is clear, we can think at higher levels of abstraction without getting into detailed implementation of modules. For deployment, fewer modules are impacted. 

Low coupling means fewer interconnections across modules. It means one module can be changed or even replaced without impacting other modules. It means modules are isolated from the implementation details of others. Low coupling aids simulation and testing. 


### How are cohesion and coupling relevant to microservices?

If improperly architected, an application based on microservices might end up being a **distributed monolith**. In such an application, microservices are chatty, calling one another often. Changes to one microservice requires changes to others. Same developers work on the codebases of many microservices. Microservices share the same database or even code. 

To solve these problems, one approach is to adopt **domain-driven design**. Restrict access using scoping rules. Use public APIs to encourage loose coupling. REST APIs shouldn't be tightly coupled to specific applications or services. Effectively, the idea is to get the boundaries right towards high cohesion and low coupling.

If a microservice relies on the physical address of another service, then there's tight location coupling. Service discovery tools can solve this. If a microservice needs to wait for another's response, there's temporal coupling. A message bus or event stream can solve this. 

For testing, unit tests created for a microservice shouldn't impact integration or end-to-end tests. To verify interfaces across services, gRPC and Avro can help. Test coupling can be minimized by mocking APIs and dependent services. 


### Which are the different types of software cohesion?

Parts of a module that collectively perform a single well-defined function is the strongest type of cohesion. There are however many other types of cohesion, some more desirable than others:   

    + **Coincidental cohesion**: Parts are not related and just happen to be in the same module. Such a module is hard to understand, maintain or reuse.
    + **Logical cohesion**: Parts relate to different entities though they perform similar logical functions. For example, mouse inputs and keyboard inputs are handled in the same class.
    + **Temporal cohesion**: Parts that are executed when something happens, such as start-up or clean-up routines. Code changes may affect many modules.
    + **Procedural cohesion**: Code that's called one after another.
    + **Sequential cohesion**: Similar to procedural cohesion with the additional constraint that the execution sequence is important. For example, call `readFile()` before calling `processData()`.
    + **Communicational cohesion**: Also called information cohesion. Parts that share or operate on the same data.
    + **Functional cohesion**: The most desirable type of cohesion. Parts work together in fulfilling a single function or purpose.

### Which are the different types of software coupling?

Data and control are two types of information flow across modules. If only some data is exchanged, the amount of coupling is probably acceptable. Exchanging lots of data and control results in a high degree of coupling. 

Here are a few types of coupling:   

    + **Content coupling**: One module modifies local variables of another module or branches execution into another module. Violates the principle of information hiding.
    + **Control coupling**: Using control flags, one module controls the execution path in another module. Makes code hard to understand.
    + **Common coupling**: Modules access global variables. Modules are bound together via shared data structures. Leads to unexpected side effects and error propagation.
    + **Stamp coupling**: A module passes an entire data structure to another when only some data members are needed.
    + **External coupling**: When interfacing with external tools, devices or libraries, module is coupled to the external device interface, communication protocol or data format.
    + **Data coupling**: Use of parameter lists to pass data from one module to another.

### Are there quantitative measures to evaluate software cohesion and coupling?

**Relation Cohesion** (H) measures average number of internal relationships per type. Given N types and R internal relationships, `H=(R+1)/N`. A suitable range is 1.5-4.0. Higher values may indicate over-coupling internally. Another measure of cohesion is **Lack of Cohesion Of Methods (LCOM)**. LCOM has range 0-1. Zero is ideal. It's variant **LCOM Henderson-Sellers (LCOM HS)** has range 0-2. An LCOM HS value exceeding one is bad. 

**Afferent coupling** (Ca) measures the number of types in other modules that depend on elements of this module. A high value implies a module with many responsibilities. **Efferent coupling** (Ce) measures the number of types from outside the module used in this module. A high value implies a module highly dependent on others. These metrics could be applied to types, fields, methods, namespaces, etc. The ratio `Ce/(Ce+Ca)` is called **Instability**, which measures a module's resilience to change. A value of 0 is ideal. 

Class inheritance in OOP is a form of tight coupling. Some relevant metrics include weighted methods per class, depth of inheritance tree, number of children and count of base classes.  

## Milestones

Jun  
1974

Stevens et al. propose a method to reduce complexity. They call it **Structured Design** or **Composite Design**. A system can be divided and simplified into smaller pieces. This improves and speeds up the writing, debugging and modifying of code. This is because each part can be considered separately. However, identifying suitable "modules" of code is not trivial. They therefore introduce the concepts of cohesion and coupling.  

Jun  
1994

Chidamber and Kemerer propose six **design metrics** to evaluate object-oriented design. They evaluate and interpret them on two real-world projects that use C++ and Smalltalk. The degree of similarity among methods is a measure of class cohesion. Class complexity can be measured by the number of methods and instance variables it contains, depth of inheritance and number of immediate descendants. Methods are a of measure communication complexity among classes. 

2001

Craig Larman in his book *Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and the Unified Process (2nd edition)* calls cohesion and coupling "the yin and yang of software engineering." Bad cohesion usually results in bad coupling, and vice versa. He also notes that,

> The level of coupling alone can't be considered in isolation from other principles such as expert and high cohesion.

2003

Eric Evans publishes a book titled *Domain-Driven Design: Tackling Complexity in the Heart of Software*. He notes that as humans and designers, it's hard for us to think or reason about many concepts at a time (high coupling). We also find it hard to understand logically unrelated ideas (low cohesion). It therefore makes sense to recognize these limitations as we design software systems. 

Jan  
2014

Software architect/designer Kirwan explains why the mantra of "loose coupling and high cohesion" may not work in practice. He points out flaws in the paper by Stevens et al. (1994). For example, maximizing cohesion is neither a clear concept nor desirable. Too many dependencies within a module gives no structural benefit. When change happens, its effect within the module could be as bad as across modules in a tightly coupled system. Thus, it's only a matter of perspective: are we looking at classes in a package or across packages?