How Cohesion And Coupling Correlate

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.

Image for post
Image for 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?

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:

Image for post
Image for post
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:

Examples of cohesion measurement
Examples of cohesion measurement
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 work against each other

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.

It’s about the level of abstraction

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.

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
Low cohesion, tight coupling
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
High cohesion, tight coupling
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
High cohesion, loose coupling
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.

But what about coupling? Did we really decrease the degree of dependencies in the system? The point is: although the absolute amount of dependencies remains the same, the coupling is tackled on different levels of abstraction. 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
Coupling on the higher level of abstraction is dramatically reduced
Coupling on the higher level of abstraction is dramatically reduced

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

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

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.

Talk to me in code!

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

  • 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.

Originally published on my blog.

Written by

Software developer and occasional blogger: https://blog.ttulka.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store