Introduction

Two phrases you will hear again and again in design discussions are:

  • “Aim for high cohesion.”

  • “Aim for low coupling.”

They sound abstract, but they directly affect how painful or pleasant your codebase feels to work with.
This article explains both concepts in beginner‑friendly terms, shows what “good” and “bad” look like, and connects them to the other principles you’ve already covered (SRP, SoC, DIP, KISS).

What Is Cohesion?

Cohesion is about what’s inside a module or class:

How closely related and focused are the responsibilities of this class or module?

  • High cohesion: everything in the class fits together around a single, clear purpose.

  • Low cohesion: the class does many unrelated things; methods and fields feel random or scattered.

You can think of a cohesive class as one where:

  • Its name makes sense.

  • Most methods deal with the same core data or concept.

  • It would be reasonable to explain it in one short sentence.

Low Cohesion Example


This class mixes:

  • Pricing

  • Email sending

  • File saving

  • Random number generation

The methods are unrelated. The class name Utility hides the fact that it has no single purpose.
This is low cohesion.

High Cohesion Example

Split each concern into its own focused class:


Now:

  • Each class has a single clear responsibility.

  • Methods within each class are closely related.

  • This is high cohesion and aligns closely with SRP.

High cohesion makes classes easier to understand, test, and reuse.

What Is Coupling?

Coupling is about what’s between modules or classes:

How strongly does this class depend on other classes?

  • High coupling: a class is tightly tied to many others; changes in one often force changes in others.

  • Low coupling: a class depends on a small, stable set of others, often via abstractions; changes are more local.

Types of coupling you’ll often see:

  • Direct instantiation (new inside methods)

  • Accessing many details of other classes

  • Relying on concrete types instead of interfaces/abstract classes

High Coupling Example


Here, OrderService is tightly coupled to:

  • CardPayment

  • EmailNotifier

  • SqlOrderRepository

To change the payment method, notification type, or storage, you must modify OrderService.
This is high coupling.

Lower Coupling Example (with Abstractions)

Introduce abstractions:


Concrete implementations:


Now OrderService depends on abstractions:


Here:

  • OrderService is still coupled to some things (it must know about payment, notification, and repository concepts).

  • But that coupling is looser because it is to abstractions, not specific implementations.

This is lower coupling, matching DIP and making the code easier to extend and test.

“High Cohesion, Low Coupling” Together

The classic guideline is:

Strive for high cohesion and low coupling.

Why these two together?

  • If cohesion is high:

    • Each class/module is focused; changes have a “natural home.”

    • It is easier to describe and reason about each piece.

  • If coupling is low:

    • Pieces can change independently.

    • You can swap implementations (e.g., a new payment method, a different database) without rewriting large parts of the system.

In the earlier example:

  • PricingService, EmailService, and FileStorage Are cohesive.

  • Using interfaces like PaymentMethod and Notifier keeps coupling low between OrderService and the rest of the system.

The combination gives you a design where:

  • Each part has a clear job.

  • Parts are connected through narrow, stable contracts.

How Coupling and Cohesion Show Up in LLD

When doing Low Level Design:

  1. Look inside each class (cohesion)

    • Does this class have one clear purpose?

    • Do its methods all relate to that purpose?

    • If method names and responsibilities feel random, cohesion is low.

  2. Look at relationships between classes (coupling)

    • Does a class import or mention many other classes?

    • Does it construct concrete implementations directly instead of depending on abstractions?

    • Do changes to one class frequently require changes to many others?

Typical LLD patterns to improve both:

  • Use SRP and Separation of Concerns to increase cohesion.

  • Use Dependency Inversion and interfaces/abstract classes to reduce coupling.

  • Use KISS & YAGNI to avoid over‑engineering while doing all this.

Simple Refactoring Checklist

When you spot messy code, ask:

  1. For cohesion (inside a class)

    • Can I split this class into two or more classes, each focused on one type of responsibility?

    • Does the class name become clearer (e.g., OrderValidator, PaymentProcessor, OrderRepository)?

  2. For coupling (between classes)

    • Can I replace direct dependencies on concrete classes with dependencies on interfaces or abstract classes?

    • Can I inject dependencies from outside (constructor/method parameters) instead of constructing them inside?

    • Can I remove unnecessary knowledge about other classes’ internal details?

Small steps in these directions gradually move your design toward high cohesion, low coupling.

Summary

Cohesion describes how focused a class or module is on a single, well‑defined responsibility.
Coupling describes how strongly different classes or modules depend on each other.

Aim for:

  • High cohesion: each class has one clear job; its methods and data all relate closely.

  • Low coupling: classes depend on as few others as possible, preferably through stable abstractions rather than concrete implementations.

In this article, you saw:

  • Plain‑language definitions of coupling and cohesion.

  • Examples of low vs high cohesion (Utility vs focused services).

  • Examples of high vs lower coupling (OrderService tied to concrete classes vs abstractions).

  • How these ideas connect with SRP, SoC, DIP, KISS, and other design principles.

  • A practical checklist you can use to improve your own low-level designs.