Skip to content

Coupling and Cohesion

The fundamental forces that shape software architecture

The Core Tension

Every architectural decision we make is fundamentally about managing two forces: coupling and cohesion. Understanding these concepts is essential because they explain why we structure code the way we do, not just how.

Coupling: The Cost of Dependencies

Coupling occurs when one piece of code depends on another. The tighter the coupling, the harder it becomes to change one piece without affecting the other.

Types of Coupling

Direct Dependencies The most obvious form—when code directly imports, calls, or references another piece of code.

typescript
// Tight coupling: OrderService directly depends on EmailService
class OrderService {
  constructor(private emailService: EmailService) {}
}

Shared Data When multiple components read and write the same data structures or database tables.

typescript
// Both services manipulate the same user record
userService.updateLastLogin(userId);
notificationService.checkUserLastLogin(userId);

Temporal Coupling When code must execute in a specific order to work correctly.

typescript
// Must call these in order
database.connect();
database.authenticate();
database.query(); // Fails if previous steps weren't done

Shared Resources When components compete for or share system resources like files, network connections, or memory.

The Cost of Coupling

  • Change Amplification: Modifying one component requires changes to all coupled components
  • Testing Complexity: Can't test components in isolation
  • Cognitive Load: Must understand multiple components to work on one
  • Deployment Risk: Can't deploy components independently

Cohesion: The Power of Relatedness

Cohesion measures how well the elements within a module belong together. High cohesion means that code that changes together, lives together.

Signs of High Cohesion

Single Purpose All code in the module works toward the same goal.

typescript
// High cohesion: All methods relate to order processing
class OrderProcessor {
  validateOrder() {}
  calculateTotals() {}
  applyDiscounts() {}
  processPayment() {}
}

Common Data Functions that operate on the same data structures are grouped together.

Temporal Cohesion Operations that happen at the same time in a business process are co-located.

Communicational Cohesion Code that works with the same input or produces related output stays together.

Benefits of High Cohesion

  • Easier to Understand: Related functionality is in one place
  • Easier to Modify: Changes are localized
  • Better Reusability: Cohesive modules have clear, focused purposes
  • Reduced Coupling: Internal coupling within a module is better than coupling between modules

The Fundamental Principle

Things that change together should live together. Things that change separately should be decoupled.

This principle drives our architectural decisions:

  1. Increase cohesion by grouping related functionality
  2. Decrease coupling between unrelated components
  3. Accept coupling within cohesive boundaries

Strategic Coupling

Not all coupling is bad. The key is being intentional about where we allow it.

Good Coupling

Within Cohesive Boundaries Components within a module that share a common purpose should be coupled.

typescript
// Good: Tightly coupled within the Order module
class Order {
  items: OrderItem[];
  customer: Customer;
  pricing: PricingStrategy;
}

To Stable Abstractions Depending on stable interfaces rather than volatile implementations.

typescript
// Good: Coupled to interface, not implementation
constructor(private repository: IOrderRepository) {}

Bad Coupling

Cross-Domain Business domains that could evolve independently are tightly coupled.

To Infrastructure Business logic depends on specific technical implementations.

Circular Dependencies Components that depend on each other create change deadlocks.

Decoupling Strategies

Dependency Inversion

Depend on abstractions, not concretions. Let high-level modules define the interfaces they need.

Event-Driven Communication

Use events to communicate between modules without direct dependencies.

typescript
// Decoupled: Order doesn't know about inventory
orderEvents.emit('orderPlaced', order);
// Inventory subscribes to events independently

Shared Kernel

Extract truly shared concepts into a stable, carefully managed shared library.

Anti-Corruption Layer

Create a translation layer between different models to prevent coupling to external systems.

The Architecture Connection

Our architectural patterns are tools for managing coupling and cohesion:

Practical Guidelines

When to Decouple

  • Different rates of change
  • Different teams working on the code
  • Different deployment schedules
  • Different business concerns
  • Different technical requirements

When to Accept Coupling

  • Within a single business transaction
  • For cohesive domain concepts
  • When the cost of decoupling exceeds the benefit
  • For stable, unlikely-to-change relationships

Warning Signs

Too Much Coupling

  • Shotgun surgery: One change requires many file modifications
  • Feature envy: Code that uses another module's data more than its own
  • Inappropriate intimacy: Classes that know too much about each other

Too Little Cohesion

  • God classes: Classes that do everything
  • Scattered functionality: Related code spread across many files
  • Data classes: Classes with only data, no behavior

The Balance

Perfect decoupling is neither achievable nor desirable. The goal is intentional, strategic coupling that:

  1. Keeps related things together (cohesion)
  2. Separates unrelated things (decoupling)
  3. Creates clear, stable boundaries
  4. Enables independent change

Remember: Architecture is the art of drawing boundaries in the right places. Coupling and cohesion tell us where those boundaries should be.

Applied In

Further Reading

  • Structured Design by Larry Constantine and Edward Yourdon - Original source of coupling and cohesion concepts
  • Coupling and Cohesion by Vladimir Khorikov
  • Clean Code by Robert C. Martin - Chapter 10: Classes