November 18, 2025
Afferent and efferent coupling, explained with numbers from real codebases
What afferent coupling (Ca) and efferent coupling (Ce) actually measure, how to read them together, and what the rest of the metrics on a Striff diagram mean: WMC, DIT, NOC, and encapsulation, illustrated with real values from Spring Framework and Apache Pinot.
Every architecture tool eventually shows you two numbers: afferent coupling and efferent coupling. Most engineers nod, vaguely recall the definitions point in opposite directions, and move on. That’s a shame, because these two numbers, read together, are the closest thing software has to a blood pressure reading for a component.
Here’s what they measure, why they matter, and, using real values from Spring Framework and Apache Pinot, how to tell a healthy number from a warning sign. At the end, we’ll decode the rest of the metric badges you’ll see on a Striff diagram.
The two directions
Both metrics count dependencies on a single component (a class, or a package). The only difference is which way the arrows point.
Afferent coupling (Ca): arrows in. How many components depend on you. This is your blast radius: if you change, this is how many places can break.
Efferent coupling (Ce): arrows out. How many components you depend on. This is your exposure: every outgoing arrow is a reason you might be forced to change.
Afferent vs. efferent: same node, opposite questions
Both numbers are real: AvroUtils has an afferent coupling of 24 in Apache Pinot's parsed scope; AbstractBeanFactory reached an efferent coupling of 102 in Spring Framework. Both come from PRs we analyzed with Striff.
The definitions go back to Robert C. Martin’s package-design metrics, which also give us the derived number worth knowing: instability, I = Ce / (Ce + Ca). A component with high Ca and low Ce is stable (hard to justify changing, easy to depend on). One with high Ce and low Ca is unstable (free to change, dangerous to depend on). Neither is bad on its own. Problems start when a component is high on both axes at once.
The four quadrants
Reading Ca and Ce together
Stable core
Interfaces, domain types, shared contracts. Everyone depends on them; they depend on little. Healthy, but every change here is expensive by design.
The danger zone
Many dependents and many dependencies: god classes, "utils" dumping grounds, accidental bridges. Fragile to change, impossible to avoid. AbstractBeanFactory lives here.
Quiet leaf
Self-contained helpers and features. Change freely; almost nothing can break.
Orchestrator
Controllers, entry points, wiring code. Volatile but safe: nothing depends on them, so their churn doesn't ripple.
The quadrant a component sits in matters more than either raw number. A Ce of 40 on an orchestrator is Tuesday; a Ce of 40 on a stable core component means every one of its many dependents inherits 40 new reasons to break.
Why the delta beats the absolute number
Here’s where most static-analysis dashboards go wrong: they report absolute values and let you stare at them. But an absolute coupling number without context is nearly meaningless. Spring’s AbstractBeanFactory has had high coupling for fifteen years and Spring works fine. What matters is where a change happens, and which direction it’s trending.
The real Spring finding makes the point:
The same +3 delta, two very different meanings
Both changes add 3 outgoing dependencies. The first is noise. The second lands on a class hundreds of components depend on, so all of them inherit three new transitive reasons to break. Same diff-size, wildly different blast radius. Risk = delta × afferent coupling, not delta alone.
The Pinot example shows the same logic on the other axis. In apache/pinot #19073, SegmentProcessorAvroUtils grew its efferent coupling from 36 to 44 in one PR, while sitting next to AvroUtils, a contract with 24 dependents, which the same PR modified. Either number alone is unremarkable. Together they describe a class becoming a bridge between the core engine and a plugin, which is exactly what Striff flagged.
The rest of the instrument panel
Coupling is two gauges on the dashboard. On a Striff diagram, every changed class carries a full row of metric badges, each with its delta since the last version. Here’s the row for a class that just absorbed a big refactor, and what each badge means:
The badges on a Striff class box, decoded
| Badge | What it measures | Why you'd care |
|---|---|---|
| WMC | Weighted method complexity: the summed cyclomatic complexity of the class's methods | The single best "is this becoming a god class?" gauge. A WMC jumping 36% in one PR means logic is pooling here instead of being distributed. |
| DIT | Depth of inheritance tree: how many ancestors the class has | Deep hierarchies make behavior hard to trace; every layer is a place logic can hide. A DIT that grows during a "simple" change deserves a look. |
| NOC | Number of children: direct subclasses | High NOC means the class is a contract many things extend. Like high Ca, it multiplies the cost of every change you make here. |
| ENC | Encapsulation ratio: the share of members that are private or protected (0 to 1) | A falling ENC means the class is exposing more of its internals, inviting exactly the kind of coupling the other badges then measure. |
| AC / EC | Afferent and efferent coupling, as above | Blast radius and exposure. The two you now know how to read. |
The badges always show the value and the change, because as with coupling, the delta in this PR matters more than the absolute number. A class at WMC 38 is unremarkable; a class that got there this week is a trend.
Read together, the panel tells a story no single metric can. Rising WMC with rising EC and falling ENC is a class absorbing responsibilities, reaching out for more collaborators, and opening its internals to do it: a god class in the making, visible three PRs before anyone would name it that in review.
How to use these numbers on a real team
Skip the thresholds. Rules like “Ce must stay under 20” produce arguments, not architecture. What works:
- Watch high-Ca components like production config. Any PR that touches a component with dozens of dependents deserves a closer look, especially when the diff looks trivial. Small diffs on high-Ca nodes are where blast-radius accidents live.
- Treat Ce growth on stable components as a smell. A stable core component that keeps gaining outgoing dependencies is migrating toward the danger-zone quadrant, one convenient import at a time.
- Track trends, not snapshots. Coupling that grew 99 → 102 this month and 96 → 99 last month is a direction, and directions compound. This matters double when AI tools multiply your PR volume: drift that took a year now takes a quarter.
That’s the part Striff automates: it parses every PR into a component graph, computes the full metric panel and its deltas on the spot, and flags only the changes whose position makes them risky: a coupling spike on a high-dependency node, not a helper gaining its fifth import. Install the browser extension and the numbers in this post show up on your own pull requests, automatically.