In Robert C. Martin's component/package metrics, what do afferent coupling (Ca), efferent coupling (Ce), and instability I = Ce / (Ca + Ce) measure, and what do I = 0 and I = 1 say about a component?
answer
- A = Arriving in, E = Exiting out
- I = Ce / (Ca + Ce), range 0..1
- I=0 stable = hard to change
- Depend toward lower I (SDP)
- Ratio hides magnitude — read Ca, Ce too
basics
~20 sCa counts things outside the component that depend on it (incoming arrows). Ce counts things it depends on (outgoing arrows). I = Ce/(Ca+Ce) runs 0 to 1: 0 = maximally stable (everyone depends on it, it depends on nobody), 1 = maximally unstable.
solid answer
~50 sCa (afferent, "arriving") is the number of external elements that depend on the component; Ce (efferent, "exiting") is the number of external elements it depends on. Instability I = Ce / (Ca + Ce) normalizes the ratio to [0, 1]. I = 0 means only incoming dependencies: nothing can force this component to change from outside, but changing it forces many others to change — it is stable, hence expensive to modify. I = 1 means only outgoing dependencies: nobody depends on it, so it is cheap to change — it is unstable/responsible-to-change. Neither extreme is inherently good. The Stable Dependencies Principle says dependencies should point in the direction of increasing stability (decreasing I): a component should only depend on components at least as stable as itself. Volatile leaf components (UI, main/composition root) should sit high in I; shared kernels and core policies low.
code
text · 8 linesDependency graph: Ca Ce I = Ce/(Ca+Ce)
Web -> Billing 0 2 1.00 (volatile leaf)
Jobs -> Billing 0 1 1.00
Billing -> Money 2 1 0.33
Money (depends on nothing)2 0 0.00 (stable core)
Arrows run 1.00 -> 0.33 -> 0.00 : decreasing I, so SDP holds.
An arrow Money -> Web (0.00 -> 1.00) would violate it.go deeper
Define Ca and Ce correctly (in vs out), state the formula, and say what the two extremes mean.
Add the Stable Dependencies Principle and a concrete example of an arrow that violates it, plus dependency inversion as the fix.
Discuss counting conventions, the ratio-hides-magnitude trap, cycles, and where static metrics are blind (runtime/data coupling).
Frame it as a governance tool: encode the rule as an automated fitness function in CI, track trends rather than absolute values, and be explicit that the metric is a proxy for change cost, not the goal.
### The vocabulary A **component** here means a deployable/releasable grouping of code — a package, module, assembly, library, or service. A **dependency** means "element X refers to element Y" (calls it, imports it, subclasses it, uses its type). Draw the components as nodes and the dependencies as arrows. - **Afferent coupling (Ca)** — *afferent = carrying toward*. The number of elements **outside** the component that depend on classes **inside** it. Arrows pointing **in**. Sometimes called "fan-in" or "incoming dependencies". - **Efferent coupling (Ce)** — *efferent = carrying away*. The number of elements **outside** the component that classes inside it depend on. Arrows pointing **out**. "Fan-out", "outgoing dependencies". Mnemonic: **A** for **A**rriving, **E** for **E**xiting. ### Instability ``` I = Ce / (Ca + Ce) (defined as 0 by convention when Ca = Ce = 0) ``` It is a ratio, so it is dimensionless and lives in [0, 1]. - **I = 0** — purely afferent. Many depend on it; it depends on nothing. Changing it ripples outward into everything, so in practice you *don't* change it. That is what "stable" means here: **hard to change**, not "bug-free" and not "good". - **I = 1** — purely efferent. It depends on others; nobody depends on it. You can rewrite it freely — nothing breaks downstream. "Unstable" = **easy to change**, not "flaky". - **I ≈ 0.5** — balanced; the component both serves and consumes. Worked example: component `Billing` is imported by `Web`, `Reporting`, `Jobs` (Ca = 3) and imports `Persistence`, `Money` (Ce = 2). I = 2 / (3 + 2) = 0.4 — moderately stable. ### Why anyone cares: the Stable Dependencies Principle (SDP) > *Depend in the direction of stability.* A component should only depend on components that are **more stable than itself** (lower I). The reason is change propagation. If a volatile component (high I) is depended on by a stable one (low I), every churn in the volatile piece forces changes into the piece that many others rely on — instability is "transmitted" into the core. Concretely: a UI module (I near 1) depending on a domain model (I near 0) is healthy. A domain model importing a UI helper is an SDP violation and shows up as an arrow pointing from low I to high I. When you must depend on something volatile, insert an **abstraction you own** (an interface in your stable component that the volatile component implements) — dependency inversion turns the arrow around. ### Counting rules and caveats - Counting granularity varies by tool: some count distinct **classes/types**, others distinct **packages/components**. Comparisons are only meaningful within one tool and one counting convention. - **I is a ratio, so it hides magnitude.** Ca=1/Ce=1 and Ca=100/Ce=100 both give I = 0.5. Always read Ca and Ce alongside I. - The metric is **static**: it sees compile-time references. It cannot see runtime coupling created by reflection, dependency injection by string name, plugins loaded dynamically, shared database tables, message topics, or shared file formats. Two services with zero static coupling can be tightly coupled through a shared schema. - I says nothing about **whether the dependencies are appropriate** — a component with Ce = 40 has a design smell (too many responsibilities) even if I looks fine. - Metrics are **signals, not verdicts**. Their best use is as a trend or as an automated architecture fitness function ("no arrow may point from I ≤ 0.2 to I ≥ 0.8") rather than a score to optimize. ### How this is used in practice Tools (JDepend, NDepend, Structure101, ArchUnit-style rules, `jdeps`, dependency-cruiser, import-linter) compute Ca/Ce/I per package and let you fail the build on violations. The typical findings: a "utils" grab-bag with huge Ca *and* huge Ce (everything depends on it and it depends on everything — a change amplifier), and cycles, where instability becomes meaningless because every component in the cycle must be released together.
- Is a component with I = 1 a problem?No. I = 1 is exactly right for a leaf that nothing should depend on — the main/composition root, the UI shell, a CLI entry point. It is only a problem if something stable starts depending on it.
- What do you do when a stable component genuinely needs a volatile one?Apply dependency inversion: define the abstraction inside the stable component and have the volatile component implement it. The source-code arrow now points from volatile to stable while the runtime call still flows outward.
- Why does instability become meaningless inside a dependency cycle?Components in a cycle must be built and released together, so none of them is independently changeable; the ratio no longer predicts change cost. Break the cycle first (extract a shared abstraction or apply dependency inversion), then read the metric.
Think of a building. The foundation has huge afferent coupling — every floor rests on it — and no efferent coupling; it is 'stable' in the sense that you cannot renovate it without evacuating the building. The paint on a top-floor wall is 'unstable': nothing rests on it, so you can repaint any weekend. You want the load path to run downward, from paint to foundation, never the reverse.
saying these in an interview costs you the question
- Reading 'stable' as 'reliable/bug-free' and 'unstable' as 'flaky' — the terms only mean hard/easy to change
- Swapping the definitions: afferent is incoming, efferent is outgoing
- Treating I = 0 as the goal for every component; a system of all-stable components cannot evolve
- Optimizing I without looking at raw Ca/Ce — a ratio can't distinguish 1-and-1 from 100-and-100
- Assuming low static coupling means low coupling, ignoring shared databases, message schemas, and reflection