Updated on 17 Jun, 202623 mins read 16 views

Introduction

Imagine two software systems.

System A:

Change one class
    ↓
20 other classes break
    ↓
10 tests fail
    ↓
Unexpected bugs appear

Developers become afraid to modify code.

Every change feels dangerous.

System B:

Change one class
    ↓
Only one module affected
    ↓
No unrelated failures
    ↓
Easy deployment

Developers confidently evolve the system.

Why are these systems so different?

The answer lies in two of the most important concepts in software engineering:

Coupling and Cohesion

These concepts are so important that nearly every software engineering principle, design pattern, architecture style, and framework eventually boils down to improve one or both.

In fact:

Good software design is largely the art of achieving low coupling and high cohesion.

Everything else is often a consequence.

Historical Context

During the Software Crisis (1960s – 1980s), engineers noticed something strange.

Two systems with similar functionality could have drastically different maintenance costs.

Example:

System A:
100,000 lines

System B:
100,000 lines

Same size.

But:

System A: Easy to modify

System B: Extremely difficult to modify

Why?

Researchers began studying software architecture.

They discovered two characteristics repeatedly appeared in successful systems:

LOW COUPLING
HIGH COHESION

These became fundamental software engineering principles.

Understanding Coupling

What Is Coupling?

Coupling measures:

How strongly one component depends on another component.

Or more simply:

How connected things are.

Real-World Analogy: Train Cars

Imagine train cars.

Loosely Coupled

[Car A] -- [Car B]

Tightly Coupled

[Car A]
   |
[Car B]
   |
[Car C]
   |
[Car D]

Removing one affect everything.

Software behaves similarly.

Formal Definition

Coupling is:

The degree of dependency between software modules.

Higher dependency:

Higher Coupling

Lower dependency:

Lower Coupling

Why Coupling Matters

Every dependency introduces risk.

Example:

class PaymentService {

};

Used by:

OrderService
InvoiceService
RefundService
NotificationService
AnalyticsService

Now changes inside PaymentService may affect many places.

Dependencies create maintenance costs.

Visualization Coupling

Low Coupling:

+---------+
| ModuleA |
+---------+

+---------+
| ModuleB |
+---------+

+---------+
| ModuleC |
+---------+

Independent modules.

High Coupling:

A → B → C → D → E
↑               ↓
+---------------+

Complex dependency network.

Changes become dangerous.

Example: Tight Coupling

Consider:

class MySQLDatabase
{
public:
    void save()
    {
    }
};
class UserService
{
private:
    MySQLDatabase database;

public:
    void registerUser()
    {
        database.save();
    }
};

Problem:

UserService depends directly on MySQL.

Suppose tomorrow:

MySQL --> PostgreSQL

Now UserService must change.

This is tight coupling.

Better Design

Introduce abstraction.

class IDatabase {
	public:
		virtual void save() = 0;
};

Implementation:

class MySQLDatabase: public IDatabase {
	public:
		void save() override
		{
			// ...
		}
};

Usage:

class UserService {
	private:
		IDatabase& database;

	public:
		UserService(IDatabase& db)
			: database(db)
		{
		}
};

Now:

UserService depends on abstraction

Coupling decreases significantly.

Symptoms of High Coupling

Frequent Ripple Effects

Change:

Class A

Unexpectedly affects:

Class B
Class C
Class D
Class E

Fear of Modification

Developers say:

Don't touch that code.

Major warning sign.

Difficult Testing

Creating one object requires:

Databases
Network
Cache
Queue
Logger

Too many dependencies.

Difficult Reuse

Class cannot be reused because it depends on everything.

Types of Coupling

1: Content Coupling (Worst)

One module directly modifies internals of another.

Example:

class User
{
public:
    std::string password;
};

External code:

user.password = "345";

Very dangerous.

2: Common Coupling

Shared global state.

globalConfig
globalDatabase
globalLogger

Everything depends on the same data.

Example:

GlobalSettings::debugMode = true;

Suddenly entire sysetm behavior changes.

3: Control Coupling

Passing flags that control behavior.

Example:

generateReport(true);

Question:

What does true mean?

Bad design.

Better:

generateSummaryReport();
generateDetailedReport();

4: Data Coupling

Passing only required information.

calculateTax(price);

Simple.

Clear.

Independent.

Desired Goal

Move toward:

LOW COUPLING

Not:

ZERO COUPLING

Important distinction.

Some dependencies are necessary.

Understanding Cohesion

What Is Cohesion?

Cohesion measures:

How strongly related the responsibilities inside a module are.

Or:

How well things belong together

Real-World Analogy: Toolbox

Good toolbox:

Hammer
Screwdriver
Wrench
Pliers

All related. High cohesion.

Bad toolbox:

Hammer
Pizza
Laptop
Dog Leash
Toothbrush

Random items. Low cohesion.

Software works exactly the same way.

Formal Definition

Cohesion is:

The degree to which elements within a module belong together.

Higher relatedness:

Higher Cohesion

Lower relatedness:

Lower Cohesion

Example: Low Cohesion

class EmployeeManager
{
public:

    void addEmployee();

    void calculateSalary();

    void sendEmail();

    void generateReport();

    void connectDatabase();

    void printInvoice();
};

Question:

What is this class responsible for?

Answer:

Everything

Low cohesion.

Example: High Cohesion

class EmployeeService
{
public:

    void addEmployee();

    void removeEmployee();

    void updateEmployee();
};

Single responsibility. Strong cohesion.

Visualizing Cohesion

Low Cohesion:

EmployeeService

 ├─ Email
 ├─ Database
 ├─ Reports
 ├─ Payroll
 ├─ Authentication

Random responsibilities.

High Cohesion:

EmployeeService

 ├─ Add Employee
 ├─ Remove Employee
 ├─ Update Employee

Everything belongs together.

Why Cohesion Matter

High cohesion improves:

Readability

Maintainability

Testability

Reusability

Because responsibilities are clear.

Cohesion Levels

Coincidental Cohesion

Completely unrelated functionality.

Example:

Utils
Helpers
CommonStuff
MiscFunctions

Typical anti-patterns.

Logical Cohesion

Related only by category.

Example:

class InputHandler
{
    handleKeyboard();
    handleMouse();
    handleTouch();
}

Better, but still weak.

Functional Cohesion

Everything contributes toward one goal.

Example:

class PaymentProcessor
{
    validatePayment();
    authorizePayment();
    capturePayment();
}

Coupling vs Cohesion

CouplingCohesion
Between modulesWithin module
Dependency measurementResponsibility measurement
Want lowWant high
Affects flexibilityAffects clarity

The Golden Rule

The fundamental design goal:

LOW COUPLING

HIGH COHESION

This combination creates maintainable systems.

Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *