skip to content

How is the instability metric I = Ce / (Ca + Ce) computed for a component, and how do you use it to check the Stable Dependencies Principle?

level: middleimportance: must knowfreq 34%

answer

  1. I = Ce / (Ca + Ce)
  2. Ca arriving, Ce exiting
  3. I=0 maximally stable, I=1 maximally unstable
  4. Along every arrow: I(X) ≥ I(Y)
  5. Lower Ce is the lever you control

basics

~20 s

Count outgoing dependencies (Ce) and incoming ones (Ca), then I = Ce / (Ca + Ce). I = 0 means maximally stable, I = 1 maximally unstable. SDP holds when each component's I is greater than or equal to the I of everything it depends on.

solid answer

~50 s

Ca (afferent coupling) is the number of components outside this one that depend on it; Ce (efferent coupling) is the number of outside components it depends on. Instability I = Ce / (Ca + Ce), ranging 0 to 1. I = 0 is maximally stable — everyone depends on it, it depends on nobody. I = 1 is maximally unstable — it depends on others, nobody depends on it. SDP is then checkable numerically: for every dependency X → Y, require I(X) ≥ I(Y). A component with I = 0.2 depending on one with I = 0.8 is a violation — the metrics say a hard-to-change component is leaning on something designed to churn. Caveats: what you count as a "component" and whether you count classes or components changes the numbers; a component with no coupling at all leaves I undefined (0/0, usually reported as 0); and the metric is a directional smell detector, not a score to optimize.

code

text · 10 lines
text
Pricing:  depended on by Checkout, Reporting, Admin  -> Ca = 3
          depends on Money, Logging                  -> Ce = 2
          I = 2 / (3 + 2) = 0.4

ReportUI: depended on by (nobody)                    -> Ca = 0
          depends on Pricing, Http, Charts           -> Ce = 3
          I = 3 / (0 + 3) = 1.0

Edge ReportUI -> Pricing : 1.0 >= 0.4  OK (depends toward stability)
Edge Pricing  -> ReportUI: 0.4 >= 1.0  VIOLATION (would depend toward volatility)

go deeper

for a junior

Get the formula and the direction right: Ce on top, 0 = stable, 1 = unstable, and arrows should go from higher I to lower I.

for a middle

Work a numeric example, name the violating edge, and say which lever (usually lowering Ce) you would pull to fix it.

for a senior

Discuss what the metric cannot see — edge weight, granularity, third-party exclusions, cycles — and use it to triage rather than to score.

for a principal

Talk about wiring the directional check into CI (allowed-dependency lists, architecture fitness functions), and about the failure mode of publishing I as a team metric.

### The two counts For a component X (a deployable/releasable unit — package, library, module, service): - **Ca — afferent coupling.** How many *outside* components depend on X. Incoming arrows. Mnemonic: **A**fferent = **A**rriving. - **Ce — efferent coupling.** How many *outside* components X depends on. Outgoing arrows. Mnemonic: **E**fferent = **E**xiting. Dependencies *inside* X do not count — internal coupling is irrelevant to how the component sits in the graph. ### The formula ``` I = Ce / (Ca + Ce) 0 ≤ I ≤ 1 ``` Read the denominator as "total coupling" and the numerator as "the part that is outgoing". So I is *the fraction of a component's couplings that point outward*. - **I = 0** — Ce = 0: the component depends on nothing outside itself, and (assuming Ca > 0) others depend on it. **Maximally stable.** Nothing can force it to change, and changing it hurts many. - **I = 1** — Ca = 0: nobody depends on it, and it depends on things. **Maximally unstable.** It can be rewritten freely; everything it uses can force it to change. - **In between** — a mix. I = 0.5 means as many things lean on it as it leans on. Worked example. Component `Pricing` is imported by `Checkout`, `Reporting` and `Admin` (Ca = 3), and itself imports `Money` and `Logging` (Ce = 2). ``` I(Pricing) = 2 / (3 + 2) = 0.4 ``` ### Turning SDP into an inequality SDP — "depend in the direction of stability" — becomes purely mechanical: > For every dependency **X → Y**: **I(X) ≥ I(Y)**. Equivalently, instability must decrease (or stay flat) as you follow arrows downward. Any edge where I increases along the arrow is an SDP violation: a more-stable component is depending on a more-volatile one. Example violation: `core-domain` has I = 0.15 and depends on `report-formatting` with I = 0.85. Following the arrow, I goes 0.15 → 0.85 — uphill. The volatile formatting component is now pinned in place by a component with dozens of dependents. ### Moving the number deliberately If you decide a component must be *more* stable (lower I), you have two levers: - **Lower Ce** — remove outgoing dependencies. Inline what you need, move the needed code in, or invert the dependency so the other side hands you an implementation of an interface you own. This is the lever you actually control. - **Raise Ca** — get more consumers. Real, but not something you can honestly "decide"; artificially adding dependents to game a metric is nonsense. To make a component *less* stable (higher I): strip its dependents (split it, or push the shared bits into a new lower component) and let it freely depend on things. ### Edge cases the formula hides - **Ca = Ce = 0.** I is 0/0 — undefined. Tools usually report 0 (or 1) by convention. An isolated component is neither stable nor unstable in any meaningful sense; ignore the number. - **Granularity dominates the result.** Split one component into three and every previously-internal edge becomes external coupling, changing everyone's I. The numbers are only comparable within one consistent definition of "component". - **Counting units.** Some tools count *classes* referenced rather than *components* referenced, which inflates Ce for chatty components. Know what your tool counts before arguing about a decimal. - **Weightless edges.** One incidental import counts the same as a hundred call sites through a core API. The metric cannot see how deep a coupling runs. - **Cycles.** Dependency cycles make the inequality unsatisfiable in both directions — you cannot have I(X) ≥ I(Y) and I(Y) ≥ I(X) except when equal. A cycle is an ADP (Acyclic Dependencies Principle) violation first; fix that before reading SDP numbers. - **Third-party and platform dependencies.** Most analyses exclude the standard library and stable external libraries from Ce; including them makes every component look unstable and drowns the signal. ### How to use it in practice Compute I per component, draw the graph, and highlight edges where I increases. Those edges are the conversation list — for each, either invert the dependency (see the Dependency Inversion fix), move the shared code, accept it explicitly with a note, or conclude the metric is misreading a genuinely frozen dependency. Treat I as a *diagnostic*, never a target: any metric turned into a KPI gets gamed.

  • What is I for a component that nothing depends on and that depends on nothing?
    Undefined — Ca + Ce = 0, so the formula divides by zero. Tools typically report 0 by convention. Practically it is an isolated component (a top-level executable with no deps, or dead code); the metric carries no information about it.
  • Can two components legitimately have the same I and depend on each other?
    The inequality I(X) ≥ I(Y) technically holds in both directions when they're equal, but mutual dependency is a cycle, which violates the Acyclic Dependencies Principle. Break the cycle first — SDP has nothing useful to say about a cyclic pair.
  • Should you set a build-time threshold like "no component may exceed I = 0.7"?
    No. High I is not a defect — volatile top-level components *should* have I near 1. The meaningful automated check is directional: fail the build when a dependency edge goes from lower I to higher I, or better, enforce explicit allowed-dependency lists.

saying these in an interview costs you the question

  • Swapping Ca and Ce in the formula (I = Ca/(Ca+Ce) is stability, not instability)
  • Thinking I = 1 is inherently bad, or that all components should approach I = 0
  • Counting a component's internal dependencies in Ca/Ce
  • Treating I as a target/KPI rather than a diagnostic
  • Comparing I values across projects with different component granularity
  • Ignoring that cycles make the SDP inequality unsatisfiable

context