Coupling measures the degree of interdependence between software modules. Low coupling means modules can change independently; high coupling means changes ripple across the system.
Core Concept
Coupling answers the question: “How much does one module know about or depend on another?”
- Low Coupling (Loose Coupling): Modules have minimal knowledge of each other
- High Coupling (Tight Coupling): Modules are heavily dependent on each other’s internals
Why Coupling Matters
Maintainability
Low coupling makes changes safer:
- Modifications localized to one module
- Fewer ripple effects across the system
- Easier to understand impact of changes
Testability
Loosely coupled code is easier to test:
- Can test modules in isolation
- Fewer dependencies to mock/stub
- Faster, more focused tests
Reusability
Low coupling enables reuse:
- Modules can be extracted independently
- Fewer dependencies to bring along
- Can be used in different contexts
Flexibility
Systems with low coupling adapt more easily:
- Can swap implementations
- Easier to add new features
- Simpler to refactor
Types of Coupling
From loosest (best) to tightest (worst):
1. No Coupling (Best)
Modules are completely independent:
// Completely independent modules
public class EmailValidator { }
public class PriceCalculator { }2. Data Coupling (Good)
Modules share only data through parameters:
// GOOD: Only shares data
public class OrderService
{
public decimal CalculateTotal(List<OrderItem> items)
{
return items.Sum(item => item.Price * item.Quantity);
}
}3. Stamp Coupling (OK)
Modules share composite data structures:
// OK: Shares a data structure
public class ReportGenerator
{
public void GenerateReport(Order order)
{
// Uses some fields of Order, not all
Console.WriteLine($"Order: {order.Id}, Total: {order.Total}");
}
}4. Control Coupling (Problematic)
One module controls the flow of another:
// BAD: Controls behavior through flags
public class DataProcessor
{
public void Process(Data data, bool useNewAlgorithm)
{
if (useNewAlgorithm)
ProcessWithNewAlgorithm(data);
else
ProcessWithOldAlgorithm(data);
}
}Better approach using polymorphism:
// GOOD: Strategy pattern removes control coupling
public interface IProcessingStrategy
{
void Process(Data data);
}
public class DataProcessor
{
public void Process(Data data, IProcessingStrategy strategy)
{
strategy.Process(data);
}
}5. External Coupling
Modules share externally imposed format/protocol:
// Coupling to external API format
public class PaymentService
{
public void ProcessPayment(PaymentData payment)
{
// Both this and caller must know PaymentData format
var externalApi = new ThirdPartyPaymentApi();
externalApi.Charge(payment);
}
}6. Common Coupling (Bad)
Modules share global data:
// BAD: Shared global state
public static class GlobalConfig
{
public static string ConnectionString;
public static int MaxRetries;
}
public class DatabaseService
{
public void Connect()
{
// Couples to global state
Connect(GlobalConfig.ConnectionString);
}
}
public class RetryHandler
{
public void Retry()
{
// Also couples to global state
for (int i = 0; i < GlobalConfig.MaxRetries; i++) { }
}
}Better approach with dependency injection:
// GOOD: Dependencies injected
public class DatabaseService
{
private readonly string _connectionString;
public DatabaseService(string connectionString)
{
_connectionString = connectionString;
}
}7. Content Coupling (Worst)
One module modifies or relies on internal workings of another:
// WORST: Direct access to internal state
public class BankAccount
{
public decimal balance; // Public field - danger!
}
public class AccountManager
{
public void Withdraw(BankAccount account, decimal amount)
{
// Directly manipulates internal state
account.balance -= amount; // No validation!
}
}Reducing Coupling
Dependency Inversion
Dependency Inversion Principle - depend on abstractions:
// BAD: High coupling to concrete class
public class OrderProcessor
{
private SqlDatabase _database = new SqlDatabase();
public void Save(Order order)
{
_database.SaveOrder(order);
}
}
// GOOD: Low coupling through abstraction
public class OrderProcessor
{
private readonly IDatabase _database;
public OrderProcessor(IDatabase database)
{
_database = database;
}
public void Save(Order order)
{
_database.SaveOrder(order);
}
}Law of Demeter
Law of Demeter prevents coupling through chaining:
// BAD: High coupling - knows too much about structure
public class OrderProcessor
{
public void Process(Order order)
{
string zipCode = order.GetCustomer().GetAddress().GetZipCode();
}
}
// GOOD: Low coupling - ask directly
public class Order
{
public string GetShippingZipCode()
{
return _customer.GetZipCode();
}
}
public class OrderProcessor
{
public void Process(Order order)
{
string zipCode = order.GetShippingZipCode();
}
}Interface Segregation
Interface Segregation Principle - don’t force dependencies on unused interfaces:
// BAD: Fat interface creates unnecessary coupling
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}
public class RobotWorker : IWorker
{
public void Work() { }
public void Eat() { throw new NotSupportedException(); }
public void Sleep() { throw new NotSupportedException(); }
}
// GOOD: Segregated interfaces reduce coupling
public interface IWorkable
{
void Work();
}
public interface IFeedable
{
void Eat();
}
public class RobotWorker : IWorkable
{
public void Work() { }
}Event-Based Communication
Reduce coupling through events/observers:
// BAD: Direct coupling
public class Order
{
private EmailService _emailService;
private InventoryService _inventoryService;
public void Complete()
{
_emailService.SendConfirmation(this);
_inventoryService.UpdateStock(this);
}
}
// GOOD: Event-based decoupling
public class Order
{
public event EventHandler<OrderCompletedEventArgs> OrderCompleted;
public void Complete()
{
OrderCompleted?.Invoke(this, new OrderCompletedEventArgs(this));
}
}
// Services register interest independently
public class EmailService
{
public void Subscribe(Order order)
{
order.OrderCompleted += (s, e) => SendConfirmation(e.Order);
}
}Measuring Coupling
Afferent and Efferent Coupling
- Afferent Coupling (Ca): Number of classes that depend on this class
- Efferent Coupling (Ce): Number of classes this class depends on
Instability (I) = Ce / (Ca + Ce)
I = 0: Maximally stable (many depend on it, it depends on few)
I = 1: Maximally unstable (few depend on it, it depends on many)
These metrics apply at both class and package levels. See Package Principles for how Robert C. Martin uses these metrics in the Stable Dependencies Principle (SDP).
Code Smell Indicators
Signs of high coupling:
- Feature Envy: Methods using another class’s data more than their own
- Inappropriate Intimacy: Classes too dependent on each other’s internals
- Message Chains:
a.b().c().d() - Shotgun Surgery: Single change requires modifications across many classes
Coupling vs Cohesion
Cohesion and coupling work together for good design:
| Goal | Internal Focus | External Focus |
|---|---|---|
| High Cohesion | Related elements together | - |
| Low Coupling | - | Independent modules |
| Result | Easy to understand | Easy to change |
// IDEAL: High cohesion within, low coupling between
public class OrderCalculator
{
// High internal cohesion
private decimal CalculateSubtotal(OrderItems items) { }
private decimal CalculateTax(decimal subtotal) { }
private decimal CalculateShipping(OrderItems items) { }
public decimal CalculateTotal(OrderItems items)
{
decimal subtotal = CalculateSubtotal(items);
decimal tax = CalculateTax(subtotal);
decimal shipping = CalculateShipping(items);
return subtotal + tax + shipping;
}
}
// Low coupling to other modules
public class Order
{
private readonly OrderCalculator _calculator;
public decimal GetTotal()
{
return _calculator.CalculateTotal(_items);
}
}Acceptable Coupling
Some coupling is necessary and acceptable:
Framework/Library Coupling
Coupling to stable frameworks is acceptable:
using System.Linq; // OK: Coupling to stable .NET framework
using System.Collections.Generic;
public class UserRepository
{
public List<User> GetActiveUsers()
{
return _users.Where(u => u.IsActive).ToList();
}
}Domain Model Coupling
Domain objects naturally couple:
// OK: Domain objects have natural relationships
public class Order
{
private Customer _customer; // Natural coupling
private List<OrderItem> _items; // Natural coupling
}Stable Interfaces
Coupling to stable, well-defined interfaces:
// OK: Coupling to stable interface
public class FileLogger : ILogger
{
public void Log(string message) { }
}Common Pitfalls
Over-Engineering
Don’t eliminate all coupling at the cost of simplicity:
// TOO MUCH: Over-abstracted
public interface IIntegerAdder
{
int Add(int a, int b);
}
public class IntegerAdderFactory
{
public IIntegerAdder Create() { }
}
// JUST RIGHT: Simple when appropriate
public static class Math
{
public static int Add(int a, int b) => a + b;
}Circular Dependencies
Classes depending on each other:
// BAD: Circular dependency
public class A
{
private B _b;
}
public class B
{
private A _a;
}
// GOOD: Break with interface or third class
public class A
{
private IB _b;
}
public class B : IB
{
}Package-Level Coupling
Coupling principles also apply at the package/component level. Robert C. Martin defines three Package Coupling Principles:
Acyclic Dependencies Principle (ADP)
“Allow no cycles in the component dependency graph.”
Package dependencies must form a Directed Acyclic Graph (DAG). Cycles make testing, deployment, and reasoning about changes extremely difficult.
Stable Dependencies Principle (SDP)
“Depend in the direction of stability.”
Packages should depend on packages that are more stable than themselves. This prevents volatile code from affecting stable foundations.
Stable Abstractions Principle (SAP)
“A component should be as abstract as it is stable.”
Stable packages should be abstract so they can be extended without modification. This principle, combined with SDP, constitutes the Dependency Inversion Principle at the package level.
See Package Principles for detailed coverage including metrics and the “Main Sequence” concept.
Resources
See Also
- Cohesion - The complementary concept
- Package Principles - Coupling principles at the package level (ADP, SDP, SAP)
- Law of Demeter - Principle for reducing coupling
- Dependency Inversion - Decouple through abstractions
- Interface Segregation - Minimize interface coupling
- Object-Oriented Design
- Design Concepts
- Good Software Practices