Fan-In / Fan-Out
debt(d5/e5/b5/t5)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpmetrics, phpmd, and phpstan — all specialist/SAST-class tools that must be explicitly configured and run. High fan-out god classes and high fan-in utility classes are not caught by the compiler or default linter rules; they require deliberate metric analysis.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix acknowledges that high fan-out signals too many responsibilities (requiring responsibility splitting) and high fan-in signals high-risk change propagation. The common_mistakes note that naively reducing fan-out by merging dependencies worsens things, implying the real fix — splitting responsibilities or introducing stable interfaces — touches multiple files and is a non-trivial refactor within or across components.
Closest to 'persistent productivity tax' (b5). The term applies across web, cli, and queue-worker contexts. A high fan-in utility module is load-bearing across the codebase (a bug breaks everything), and high fan-out god classes slow down many work streams as they become coordination hubs. However, it does not quite reach b7 because not every change in the codebase is necessarily shaped by a single fan-in/fan-out decision.
Closest to 'notable trap' (t5). The misconception field explicitly states the canonical wrong belief: 'High fan-in is always desirable and high fan-out is always bad.' This is a documented gotcha — high fan-in is good for reuse but risky for change impact — that most developers eventually learn. It contradicts an intuitive but oversimplified reading of the metric, earning a t5 rather than a minor edge-case t3.
Also Known As
TL;DR
Explanation
Fan-in (afferent coupling) counts how many modules depend on a given module — high fan-in means the module is widely used and changes to it are high-risk. Fan-out (efferent coupling) counts how many modules a given module depends on — high fan-out means the module is tightly coupled to many others and is fragile to external changes. The Instability metric (fan-out / (fan-in + fan-out)) measures how likely a module is to change. Stable, core domain modules should have low instability (high fan-in, low fan-out); infrastructure adapters should have high instability.
Common Misconception
Why It Matters
Common Mistakes
- Not measuring fan-out when adding dependencies to a class — it gradually becomes a coordination hub.
- High fan-in utility functions with no tests — a bug there breaks everything that depends on it.
- Confusing fan-out with coupling — fan-out to stable interfaces is less dangerous than fan-out to concrete implementations.
- Reducing fan-out by merging dependencies rather than splitting responsibilities — lowers fan-out but increases class size.
Code Examples
// High fan-out — depends on 7+ classes, fragile to changes in any:
class OrderProcessor {
public function __construct(
private DB $db, private Mailer $mail, private Logger $log,
private Cache $cache, private PaymentGateway $pay,
private InventoryService $inv, private TaxCalculator $tax
) {} // Fan-out of 7 — consider splitting responsibilities
}
// High fan-out (too many dependencies) makes a class fragile:
class PaymentService {
// Fan-out of 6 — breaks when any dependency changes
public function __construct(
private StripeClient $stripe,
private PayPalClient $paypal,
private Logger $logger,
private Mailer $mailer,
private MetricsCollector $metrics,
private FraudDetector $fraud,
) {}
}
// Reduce fan-out — group related dependencies:
class PaymentGatewayRegistry { /* wraps stripe + paypal */ }
class PaymentEventHandler { /* wraps logger + mailer + metrics */ }
class PaymentService { // fan-out of 3
public function __construct(
private PaymentGatewayRegistry $gateways,
private PaymentEventHandler $events,
private FraudDetector $fraud,
) {}
}