How Cohesion And Coupling Correlate
By: Tomas Tulka
Tags: Programming, Software Development, Design Patterns, Software Engineering, Software Architecture

As I was finishing my blog post about defining service boundaries, I had a very strong feeling that there must be some abstract concept of what I was trying to explain on concrete examples…

Of course, there is! It’s the concept of cohesion and coupling I will discuss in this post.

In the rain (cut) by Franz Marc

Let’s start with little definitions:

Cohesion: the degree to which the elements inside a module belong together.

Coupling: the degree of interdependence between software modules.

High cohesion and loose coupling are the most important principles in software engineering. They manifest themselves everywhere from code to team organization.

Cohesion and coupling are tightly related. Why are they so important? Both help us reduce complexity, the true fun killer of software development.

To a lot of people, sadly, the concepts sound too academic and are therefore often poorly understood.

What is cohesion, anyway?

Tough question. The definition is pretty broad and there are several interpretations out there. Not all of them are necessarily wrong, the valid question is: which one is the most beneficial? I use the following definition as I believe it always leads to cohesive components with tight coupling inside and loose coupling outside, which is exactly what we want:

The degree of cohesion of a component by a particular key equals the number of elements cohesive by the key within the component divided by the sum of the total number of elements cohesive by the key in the whole system and the number of elements not cohesive by the key inside the component.

This long definition can be expressed as a simple formula:

The cohesion formula

Where c stands for the component, k stands for the key, and N stands for the number of elements. Obviously, the maximal cohesion of a component is equal to one. This is what we strive for.

I want to emphasize that cohesion doesn’t depend on the number of connections between elements, that’s what coupling is all about. Cohesion is rather about belonging together. However, cohesive components do tend to have a higher degree of coupling within the component, but that’s just a symptom of high cohesion, not the cause.

The definition above might look complicated, but it’s rather quite easy. Let’s illustrate it with some examples. We measure the degree of cohesion by the violet key for the components bordered with a dashed line in the following systems:

Example measurements of cohesion

Functionality (business) is always the right key to use. Violet and blue can stand for sales and accounting, a product and an invoice, or user registration and ordering.

Notice that my definition may be a bit oversimplified as the boundaries are not always as solid and obvious. This is why business experts must be involved.

Myth busted

Cohesion and coupling are almost always discussed together as they tightly correlate. The relation is sometimes a source of confusion as well, although its understanding is very useful to gain the most for the software system under development.

A typical myth, I often hear people believe in, puts cohesion and coupling in opposition. Practically, they say that “the higher cohesion the tighter coupling”. I’ll show you how wrong this statement is.

This is usually illustrated with an example: Consider the highest possible cohesion of the system where every module is represented by a single line of code (or a single function, an object with a single method, etc.). Such a degree of cohesion will inevitably increase the coupling between modules to the maximum.

As the conclusion is true, there is a small problem in the prerequisite. To find it out, we have to recall the definition of cohesion once again. It talks about belonging together, the strength of relationship of elements, and a common purpose.

What does it mean in practice? In fact, splitting elements that belong together makes cohesion actually lower. So, in the example above, the system really doesn’t have the highest possible cohesion, in the opposite: breaking modules into the smallest possible elements will separate related concepts and will lead to a pretty low cohesion.

The moral here is: Cohesion is not something you can create automatically. Cohesion is discovered in a particular context. That’s why it is so hard for cohesion to be reliably measured. We will discuss this in detail later, stay tuned.

Cohesion and coupling

Let me show you some pictures. In each figure below, there are the very same elements with the very same dependencies. Those are further differently organized. Related domain concepts are represented with the same color:

Low cohesion, tight coupling

Elements in the first picture have no explicit boundaries, it’s an example of so-called coincidental cohesion. Such architecture is known as the Big Ball of Mud or the God Object (in OOP code).

High cohesion, tight coupling

The second picture shows a system with three modules and a lot of dependencies between them. Although the modules are highly cohesive, they are cohesive by the wrong key. This happens when code is organized by other than a domain relationship. A typical example is a logical organization of code in the Layered Architecture: just image modules such as controllers, repositories, services, etc. Have you seen these already somewhere? Hell yeah!

High cohesion, loose coupling

The system in the third picture shows the ideal case: correctly organized modules leading to high cohesion and loose coupling. The right key for organization is functionality, in other words, a business domain. The domain defines abstractions with a stable purpose the cohesion is driven upon. By the way, that’s the main idea of the Domain-Driven Design.

Focus on cohesion, not coupling

We exhausted all variants except one: a system with low cohesion and loose coupling. Is it even possible to have such an architecture? Unfortunately, it is, and it’s actually pretty common.

Systems with low cohesion and loose coupling are results of incorrect understanding of the domain and applying purely technical approaches to decouple the modules in an arbitrary way. Interfaces everywhere with no abstraction representing a domain purpose are typical for systems built in this way.

Misuse of interfaces won’t actually reduce coupling anyway, it just moves it into the runtime.

Striving for loose coupling at any cost can (and will) harm cohesion. As loose coupling is driven by high cohesion, we should strive for high cohesion in the first place.

Level of abstraction

Yes, high cohesion doesn’t only make the system easy to understand and change, it also reduces the level of coupling.

How is this even possible? Common sense says that the dependencies don’t disappear simply by reorganizing elements. While this is true for the overall system dependencies, high cohesion does reduce dependencies on a higher level of abstraction.

That is, although the absolute amount of dependencies remains the same, the coupling is tackled on different levels of abstraction.

“The whole is greater than the sum of the parts.” ~ Aristotle

Indeed, we can ignore the interdependencies inside modules getting so a simplified big picture with only three loosely coupled elements:

Coupling on the higher level of abstraction is dramatically reduced

Neat. As we see, high cohesion actually results in loose coupling!

Talk to me in code!

Pictures are nice, but as software developers, we trust only code, don’t we? Alright, I have some code for you. Consider a simple class for a Book Store (in JavaScript, whatever):

class BookStore {

add(book) { … }

remove(book) { … }

sale(book) { … }

receiptFor(book) { … }

}

This class does literally everything. Its cohesion is pretty low and all clients, whatever their needs are, will be coupled to it. It’s an example of a God Object. We can do better:

class Inventory {

add(book) { … }

remove(book) { … }

}



class Sales {

sale(book) { … }

receiptFor(book) { … }

}

The Inventory class looks fine, but what about Sales ? Must sales and accounting really be so tightly related? Maybe it’d be better to split the functionalities into more cohesive classes:

class Sales {

sale(book) { … }

}



class Accounting {

receiptFor(book) { … }

}

But what if our Book Store is just a small family business with one clerk doing sales together with accounting on one old cash desk? We just hit the nail on the head: we can’t really know what the right cohesion key is unless we know the domain really well. True cohesion is defined by the clients. High cohesion is achieved when there’s no way to split the module any further while still satisfying the client’s needs. By the way, this is exactly what the Single Responsibility Principle teaches us.

Conclusion

High cohesion and loose coupling are the main design drivers towards simple system architecture, that is easy to understand, change, and maintain. High cohesion and loose coupling help us reduce accidental complexity and create modules with well-defined boundaries.

Coupling is about connections, cohesion is about belonging together.

Cohesion can’t be created automatically, instead it’s discovered in a context.

Cohesion is defined by the clients.

True cohesion is domain-driven.

High cohesion results in loose coupling.

High cohesion is to die for. It enables all others, loose coupling included.