Add new comment

Coupling and Cohesion: Two Concepts Every Developer Should Understand

Lately, I've been spending quite a lot of time learning about design patterns and software architecture.

And the deeper I go, the more often I come across two concepts that seem to be behind almost everything: coupling and cohesion.

They may sound abstract at first, but they actually describe two very practical things:

How strongly are different parts of the system dependent on each other, and how well do the responsibilities inside a component belong together?

Coupling

Coupling describes how strongly one component depends on another.

For example, imagine a service that directly creates a specific payment gateway:

class OrderService
{
    public function pay(Order $order): void
    {
        $gateway = new StripePaymentGateway();
        $gateway->charge($order->total());
    }
}

OrderService is now tightly coupled to StripePaymentGateway.

If we want to switch to PayPal, use a test double, or introduce another payment provider, we have to modify OrderService.

We can reduce this coupling by depending on an abstraction and using dependency injection:

class OrderService
{
    public function __construct(
        private PaymentGateway $gateway
    ) {}
 
    public function pay(Order $order): void
    {
        $this->gateway->charge($order->total());
    }
}

Now OrderService doesn't care which payment gateway is actually being used.

This is directly related to SOLID, especially the Dependency Inversion Principle.

Cohesion

If coupling is about the relationship between components, cohesion is about what happens inside a component.

A simple question to ask is:

Do the things inside this class actually belong together?

Consider this class:

class UserService
{
    public function register(): void {}
    public function sendEmail(): void {}
    public function generatePdf(): void {}
    public function resizeImage(): void {}
}

Everything may work perfectly fine, but the class has low cohesion.

It contains several unrelated responsibilities.

A better design would separate them:

UserService
EmailService
PdfGenerator
ImageResizer

Each component has a much more focused purpose.

And once again, we arrive at SOLID, particularly the Single Responsibility Principle.

How does this relate to design patterns?

Quite a few design patterns can be understood through the concepts of coupling and cohesion.

Dependency Injection helps reduce coupling.

Strategy allows us to replace implementations without changing the code that uses them.

Facade hides the complexity of a subsystem and reduces the number of dependencies a client needs to know about.

Mediator can reduce the number of direct relationships between objects.

And principles such as Single Responsibility help us create components with higher cohesion.

So, in a simplified form:

High Cohesion
      +
Low Coupling
      ↓
More maintainable architecture

This doesn't mean that we should eliminate all dependencies. Dependencies are a normal and necessary part of software.

The real goal is to keep them intentional, manageable, and appropriately structured.

Why should developers care about this?

Understanding coupling and cohesion gives us a different way to look at code.

Instead of asking only:

“Does this code work?”

we can start asking:

Why does changing one class require changes in five other places?
Why does this class know so much about other components?
Why are so many unrelated responsibilities combined here?
How difficult would it be to replace this implementation?
How easy is this component to test in isolation?

These questions move us from simply writing classes to designing systems.

And that's one of the reasons I think it's important to study not only design patterns themselves, but also the fundamental concepts behind them.

Low coupling and high cohesion are simple ideas, but they provide a very useful mental model for designing better PHP applications — and software in general.

CAPTCHA
Spam protection
Target Image