What is the "Main Sequence" in component design, how is the distance metric D = |A + I - 1| interpreted, and what are the Zone of Pain and Zone of Uselessness?
answer
- A vertical, I horizontal, unit square
- Main Sequence: A + I = 1
- D = |A + I − 1|, 0 is on the line
- Lower-left = Zone of Pain (stable + concrete)
- Upper-right = Zone of Uselessness
basics
~20 sPlot abstractness A against instability I. The Main Sequence is the line A + I = 1 — the healthy balance. D = |A + I - 1| measures how far off it a component sits (0 = on the line). The two bad corners are stable-and-concrete (Zone of Pain) and abstract-with-no-dependents (Zone of Uselessness).
solid answer
~60 sAbstractness `A = Na/Nc` and instability `I = Ce/(Ca+Ce)` both live in [0,1], so every component is a point in the unit square. Perfect SAP compliance would sit at (0,1) — maximally stable, maximally abstract — or (1,0) — maximally unstable, maximally concrete. Real components need a middle ground, so Martin defines the **Main Sequence**, the line `A + I = 1` connecting those two ideals, and the **distance metric `D = |A + I − 1|`** (0 = on the line, 1 = maximally off). Components far off in the low-A/low-I corner are in the **Zone of Pain**: stable *and* concrete — everyone depends on them, nothing can be extended without editing them. Components in the high-A/high-I corner are in the **Zone of Uselessness**: pure abstractions nobody depends on. In practice track D's mean and variance across components over time, and treat outliers as candidates for review — not as automatic violations, since non-volatile concrete components (string/math libraries, frozen codecs) can sit in the Zone of Pain harmlessly.
code
text · 8 lines# Per component: Na/Nc abstract types, Ca/Ce couplings
A = Na / Nc # abstractness [0..1]
I = Ce / (Ca + Ce) # instability [0..1]
D = abs(A + I - 1) # distance from Main Sequence [0..1]
# core-contracts : Na=8 Nc=9 Ca=41 Ce=0 -> A=0.89 I=0.00 D=0.11 healthy
# shared-utils : Na=0 Nc=57 Ca=38 Ce=4 -> A=0.00 I=0.10 D=0.90 Zone of Pain
# legacy-spi : Na=6 Nc=6 Ca=0 Ce=3 -> A=1.00 I=1.00 D=1.00 Zone of Uselessnessgo deeper
Know the axes (A vertical, I horizontal), that the Main Sequence is A + I = 1, and name the two bad corners with one example each.
Add the D formula and interpretation, and explain why the two corners are bad in terms of rigidity and wasted abstraction.
Discuss using mean/standard deviation and trend over releases rather than absolute thresholds, and the volatility caveat that makes some Zone-of-Pain components harmless.
Treat A/I/D as a diagnostic overlay on top of churn and ownership data; drive real enforcement through dependency-direction rules and deliberate api/impl component boundaries, and be explicit about what the metrics cannot see (runtime coupling, dependency weight, abstraction quality).
### Setting up the plot Two per-component metrics, both normalized to `[0, 1]`: - **Abstractness `A = Na / Nc`** — abstract types (interfaces, abstract classes) over total types. 0 = all implementation, 1 = all contracts. - **Instability `I = Ce / (Ca + Ce)`** — outgoing couplings over total couplings. `Ca` = classes outside that depend on this component; `Ce` = classes inside that depend outward. `I = 0` = maximally **stable** (lots of dependents, no outgoing dependencies — hard to change); `I = 1` = maximally **unstable** (no dependents — free to change). Plot A on the vertical axis and I on the horizontal. Every component is a point in a unit square. ### The two ideal corners and the line between them - **(I=0, A=1)** — maximally stable *and* maximally abstract: the perfect "contracts" component. Everyone depends on it; it depends on nothing; it holds only interfaces, so new behaviour is added elsewhere. - **(I=1, A=0)** — maximally unstable *and* maximally concrete: the perfect "leaf implementation" component. Nothing depends on it; it's pure implementation and free to change. Most components cannot be at either extreme — a domain module reasonably has both contracts and entities, both dependents and dependencies. Martin therefore defines the **Main Sequence** (a name borrowed from the stellar classification diagram): the straight line ``` A + I = 1 ``` connecting (0,1) to (1,0). Sitting *on* it means abstractness is proportional to stability — SAP satisfied at whatever mix you happen to have. ### The distance metric **Normalized distance from the Main Sequence:** ``` D = | A + I − 1 | # 0 = on the line, 1 = as far off as possible ``` (Martin's original formulation divided by √2 to get true perpendicular Euclidean distance; the normalized form above, sometimes written D′, is what tools report today. If a tool reports a max of ~0.707, it's using the un-normalized version.) Useful practice: compute D for every component, then look at the **mean and standard deviation** across the system, and flag components more than roughly one standard deviation above the mean. Better still, plot D per component **over successive releases** — a component drifting away from the line is decaying, which is more actionable than any absolute threshold. ### The two bad corners **Zone of Pain** — lower-left, `A ≈ 0, I ≈ 0`: **stable and concrete.** - Many components depend on it, so changing it is expensive; and it's pure implementation, so changing it is the *only* way to get new behaviour. Maximum rigidity. - Typical inhabitants: a shared relational database schema (every service depends on it; a schema is inherently concrete), a `common-utils` jar of concrete helpers, a shared DTO/model library, a widely referenced concrete framework base class. - **Crucial caveat:** pain is proportional to **volatility**. A stable concrete component that genuinely never changes — a string class, a math library, a frozen wire-format codec — sits deep in the Zone of Pain and causes no harm at all. Martin explicitly notes this. So overlay version-control churn: *stable + concrete + churning* is the real emergency. **Zone of Uselessness** — upper-right, `A ≈ 1, I ≈ 1`: **abstract with no dependents.** - Maximally abstract but nobody depends on it, so the abstraction shields no one. Usually leftover interfaces from a removed feature, speculative "framework" layers, or an over-designed plugin API with a single implementation and no callers. - Cost without benefit: indirection, extra files, harder navigation, and dead weight in the build. ### What the metrics cannot see 1. **Volatility** — the whole reason stability matters, yet neither A nor I measures it. Pair with churn. 2. **Dependency weight** — Ca/Ce count arrows, not how deeply coupled each one is. Depending on one utility function counts the same as depending on twelve internal classes. 3. **Runtime coupling** — HTTP calls, queues, shared databases, reflection, and dependency-injection wiring create real coupling invisible to static import analysis. 4. **Abstraction quality** — a god interface counts the same as segregated ones. 5. **Domain reality** — a UI-facing or composition-root component *should* be unstable and concrete; measuring it against a global target is meaningless. ### Bottom line The A/I plot is a **diagnostic instrument**, not a compliance gate. Use it to open conversations — "this module has 40 dependents, zero abstractions, and 200 commits this quarter" — and enforce actual architectural intent with dependency rules (ArchUnit, Spring Modulith, dependency-cruiser, ESLint boundaries) instead of chasing a D target in CI.
- You find a component with D = 0.9 sitting in the Zone of Pain. Is that automatically a defect to fix?No. Ask whether it is volatile. A stable concrete component that never changes — a math library, a string type, a frozen protocol codec — is harmless there, because the cost SAP prices in is the cost of *change*. Overlay commit history: if it's high-Ca, low-A, and churning every sprint, that's the real problem, and the fix is to extract stable interfaces into a contracts component and push volatile implementation into unstable ones.
- Would you enforce a maximum D in CI?Generally no. D is easy to game (add empty interfaces), blind to volatility, dependency weight, and runtime coupling, and legitimately high for composition roots and UI adapters. Track it as a trend and review outliers; enforce actual dependency *direction* with structural tests such as ArchUnit, Spring Modulith verification, or dependency-cruiser rules.
- Some tools report a maximum distance of about 0.707 instead of 1. Why?They use Martin's original perpendicular Euclidean distance, D = |A + I − 1| / √2, whose maximum is 1/√2 ≈ 0.707. The commonly reported normalized form drops the divisor so the range is [0,1]. Same ordering, different scale — don't compare thresholds across tools.
It's a balance beam between two safe ends. One safe end is a load-bearing socket everything plugs into — universal, unopinionated, and never moved. The other safe end is a plug: highly specific, and safe to redesign because nothing plugs into it. Fall off toward 'concrete thing everyone plugs into' and every change breaks the building; fall off toward 'universal socket with nothing plugged in' and you've built a beautifully engineered connector for nobody.
saying these in an interview costs you the question
- Calling the Zone of Pain automatically a bug without checking whether the component is volatile
- Thinking the Main Sequence is A + I = 0, or that the ideal is A = I
- Treating D as a CI gate and boosting A with empty interfaces to pass it
- Assuming every component should sit exactly on the line — composition roots and UI adapters legitimately don't
- Believing Ca/Ce capture runtime coupling such as HTTP calls, queues, or a shared database