Coupling — how much one module depends on the internals of another
Definition
Coupling measures how much one module, class, or component depends on the internal details of another. High coupling means a change in one place ripples unpredictably through others; low coupling means modules interact only through stable, narrow interfaces. The goal is not zero coupling — every system has dependencies — but to ensure those dependencies point toward abstractions rather than concrete implementations.
In Practice
<?php
// HIGH coupling: UserService reaches into the internals of OrderRepository
class UserService
{
public function __construct(
private OrderRepository $orders
) {}
public function totalSpent(int $userId): float
{
// Directly accesses the repository's internal PDO property — tight coupling
$rows = $this->orders->pdo->query("SELECT SUM(total) FROM orders WHERE user_id = $userId");
return (float) $rows->fetchColumn();
}
}
// LOW coupling: UserService depends only on the public contract
class UserService
{
public function __construct(
private OrderRepositoryInterface $orders
) {}
public function totalSpent(int $userId): float
{
return $this->orders->sumTotalForUser($userId);
}
}In practice, coupling manifests in many forms: a class that imports a concrete implementation instead of an interface (afferent coupling), a class whose constructor creates its own dependencies with new (hard-coded coupling), or a class that knows the database column names of another model's table (data coupling). Laravel's dependency injection container is specifically designed to keep coupling low by letting you bind interfaces to implementations — swap the implementation and nothing else changes.
In Context
In interviews, coupling usually comes up when a candidate is asked to review a piece of code or explain why a design is hard to test. The canonical answer is: "It's tightly coupled — I can't test UserService without a real database because it's wired to a concrete OrderRepository." The fix is always the same: depend on an interface, inject the dependency.
The common misconception is treating coupling as binary. Coupling exists on a spectrum, and some coupling is necessary and correct. Coupling to a stable standard library or a well-defined PSR interface is acceptable. Coupling to the internal implementation of a mutable third-party class is risky.
Related terms: cohesion (the opposite axis — how well a module's own responsibilities fit together), dependency inversion (the SOLID principle that says high-level modules should not depend on low-level modules, both should depend on abstractions), and afferent/efferent coupling (fan-in vs fan-out at the module level).