skip to content

How do the Stable Dependencies Principle and the Stable Abstractions Principle combine into the "main sequence", and what do the Zone of Pain and Zone of Uselessness mean?

level: seniorimportance: should knowfreq 24%

answer

  1. SDP: depend toward stability; SAP: stable ⇒ abstract
  2. A = abstract types / total types
  3. Main sequence A + I = 1, D = |A+I−1|
  4. (0,0) Zone of Pain — rigid, everyone depends
  5. (1,1) Zone of Uselessness — unused abstractions

basics

~20 s

SDP says depend toward stability; SAP says stable components should be abstract. Plot abstractness A against instability I: healthy components sit near the line A + I = 1 (the main sequence). Concrete-and-stable is the Zone of Pain; abstract-and-unstable is the Zone of Uselessness.

solid answer

~60 s

SDP alone creates a hazard: a maximally stable component is hard to change, so if it is full of concrete detail the system becomes rigid. The Stable Abstractions Principle (SAP) closes the loop — a component should be as abstract as it is stable — so that hard-to-modify components can still be *extended* (Open/Closed) rather than edited. Abstractness A = (abstract types in the component) / (total types), from 0 (all concrete) to 1 (all interfaces/abstract). Plotting A against I = Ce/(Ca+Ce), the good region is the line from (I=1, A=0) to (I=0, A=1), i.e. **A + I = 1**, called the main sequence. Distance from it is D = |A + I − 1|, ideally near 0. Near (0,0) — stable and concrete — is the **Zone of Pain**: everyone depends on it and it cannot be extended, so every change is expensive. Near (1,1) — abstract with no dependents — is the **Zone of Uselessness**: abstractions nobody implements or calls. Both are diagnostics, and legitimate exceptions exist (a mature database driver, a string library) that sit in the Zone of Pain harmlessly because they never change.

go deeper

for a junior

Know the two principles in one line each and that the good corners are stable+abstract and unstable+concrete.

for a middle

Give the formulas for A and D, place both zones correctly on the chart, and describe an example component in each.

for a senior

Explain why SAP is needed (SDP alone breeds rigidity; Open/Closed makes stability safe), and give a legitimate Zone-of-Pain inhabitant with the volatility argument.

for a principal

Critique the metrics — language-dependence of A, granularity sensitivity, gameability of D — and describe using D as a trend and a review prompt rather than a gate.

### Why SDP needs a partner SDP tells you which way arrows point but says nothing about *what is inside* a component. Follow SDP alone and you converge on a few maximally stable components at the bottom of the graph — extremely hard to change, by construction. If those components are packed with concrete, volatile business logic, you have built a system whose most change-resistant parts are exactly the ones the business wants to change. That is rigidity. ### Stable Abstractions Principle (SAP) > **SAP: a component should be as abstract as it is stable.** An **abstract type** is one that cannot be instantiated on its own and carries no (or minimal) implementation — an interface, an abstract class, a protocol, a trait, a pure signature. Depending on an abstract type couples you to a *shape*, not to a decision. The reason SAP works is the **Open/Closed Principle** — software should be open for extension, closed for modification. An abstract, stable component can gain new behaviour by someone writing a new implementation *elsewhere*, with no edit to the stable component and therefore no blast radius. So high stability stops being a liability. **Abstractness metric:** ``` A = (number of abstract types in the component) / (total number of types in the component) ``` A = 0: entirely concrete. A = 1: nothing but abstractions. ### The graph Put I (instability, 0 → 1) on the x-axis and A (abstractness, 0 → 1) on the y-axis. Two corners are obviously bad and two are obviously fine: - **(I=0, A=1)** — maximally stable and maximally abstract. Everyone depends on it; it is pure interface. Ideal for a contract/port component. - **(I=1, A=0)** — maximally unstable and fully concrete. Nobody depends on it; it is all detail. Ideal for a UI screen, a `main`, a job script. The **main sequence** is the straight line joining those two good corners: ``` A + I = 1 ``` Components should sit *on or near* it. Distance from the line: ``` D = | A + I − 1 | 0 ≤ D ≤ 1, lower is better ``` (Some texts use a signed normalized form D′ = (A + I − 1); the absolute version is the common one.) ### The two bad corners **Zone of Pain — near (I=0, A=0): stable and concrete.** Many components depend on it, and it is rigid — you cannot extend it, only edit it, and every edit ripples across all dependents. The archetype is a big shared "utils"/"common" library of concrete helpers that the whole system imports, or a concrete database schema that everything is written against. Every change is expensive; teams work around it, fork it, or add flags. *But the zone has legitimate inhabitants.* A component that is concrete and stable but **genuinely never changes** is harmless — the pain only materializes when change arrives. Classic examples: a mature string/date library, a stable third-party database driver, a frozen protocol codec. The heuristic to apply is volatility, not just the coordinates. **Zone of Uselessness — near (I=1, A=1): abstract and unstable.** Full of abstract types that nobody depends on. These are leftover interfaces, speculative extension points nobody implemented, abstractions written "in case we swap X" for a swap that never came. They are dead weight: cognitive load, build time, and false signals of flexibility. The fix is usually deletion. ### Reading the picture in practice - Compute A, I and D for every component; sort by D descending. - High D near the origin → candidate rigidity. Ask: does this actually change? If yes, extract interfaces (raise A) or split out the volatile concrete parts into higher-I components. - High D near (1,1) → candidate dead abstraction. Ask: who implements this, who calls it? If nobody, delete. - Track D over time rather than as an absolute pass/fail; a component drifting away from the line is more interesting than one that has always sat slightly off it. ### Honest limitations - **A is crude.** Counting types treats a one-method interface and a fifty-method god interface identically, and languages differ in what counts as abstract (Kotlin/Java interfaces, Go implicit interfaces, Python duck typing with no declared abstract type at all). In a duck-typed language A may be near-meaningless without discipline like explicit protocol/ABC declarations. - **The main sequence is a heuristic, not physics.** There is no theorem that A + I = 1 is optimal; it is an empirically useful line drawn between two agreed-good corners. - **Granularity again.** How you carve components moves every point on the chart. - **Do not target D in CI.** People raise A by adding pointless interfaces — which is precisely how you manufacture the Zone of Uselessness while improving the score.

  • Give a component that legitimately sits deep in the Zone of Pain.
    A mature, frozen third-party library — a database driver, a compression codec, a standard string/date library. It is concrete and everything depends on it, but its volatility is effectively zero, so the cost the zone predicts never gets charged. The zone flags *risk*, and the risk only realizes when change happens.
  • How would you move a component out of the Zone of Pain?
    Raise abstractness where consumers actually need variability — extract the interfaces they depend on into a stable abstract component — and push the concrete, volatile implementation up into a higher-I component that implements those interfaces. In other words, apply DIP; A rises for the depended-on piece and the churn moves to a component nobody depends on.
  • Why is chasing a low D value in CI a bad idea?
    D is trivially gamed by adding interfaces nobody implements, which raises A and lowers D while creating exactly the dead abstraction the Zone of Uselessness warns about. D is a conversation starter for a human reviewing a specific component, and most useful as a trend, not a gate.

Think of the load-bearing structure of a building versus the furniture. Load-bearing elements are stable and standardized (abstract interfaces — bolt patterns, standard beam profiles) so you can attach anything to them without recutting the frame. Furniture is concrete and freely rearranged. A load-bearing wall built as bespoke concrete with the wiring cast inside it is the Zone of Pain; an elaborate mounting standard with nothing ever mounted on it is the Zone of Uselessness.

saying these in an interview costs you the question

  • Confusing the two zones (placing 'abstract with no dependents' in the Zone of Pain)
  • Claiming every component must lie exactly on the main sequence
  • Treating any Zone of Pain occupant as a defect, ignoring whether it actually changes
  • Defining A as "number of interfaces" without dividing by total types
  • Saying SAP means stable components should have no code at all
  • Adding interfaces purely to improve the metric

context