Define abstractness (A) for a component, and explain the "main sequence" A + I = 1, the distance metric D = |A + I - 1|, and what the zone of pain and zone of uselessness are.
answer
- A = abstract types / total types
- Main sequence: A + I = 1
- D = |A + I − 1|
- (0,0) zone of pain: concrete + depended-on
- (1,1) zone of uselessness: abstract + unused
basics
~20 sA = abstract types / total types in a component (0 = all concrete, 1 = all interfaces). The main sequence is the line A + I = 1: stable components should be abstract, unstable ones concrete. D = |A + I - 1| is how far you sit from that line — smaller is better.
solid answer
~50 sAbstractness A = (number of abstract classes and interfaces) / (total number of types) in a component, in [0, 1]. Paired with instability I = Ce/(Ca+Ce), it forms a two-dimensional map. The **main sequence** is the line A + I = 1: components that many others depend on (I near 0) should be mostly abstract, so dependents bind to interfaces and the component can be extended without modification; components nobody depends on (I near 1) should be concrete implementations. **Distance from the main sequence** D = |A + I − 1| (or normalized D′ = |A + I − 1| / √2 for perpendicular distance) measures the violation. Two corners are pathological: the **zone of pain** (A≈0, I≈0) — concrete and heavily depended upon, e.g. a concrete database schema or a utility class everyone calls: rigid, change-resistant, hard to extend; and the **zone of uselessness** (A≈1, I≈1) — abstract with no implementers and no dependents: dead abstraction. Treat D as a smell detector, not a target.
code
text · 7 linesComponent A I D=|A+I-1| Reading
----------------------------------------------------------
domain-api 0.95 0.05 0.00 on the main sequence
legacy-utils 0.00 0.10 0.90 ZONE OF PAIN
plugin-spi 1.00 0.95 0.95 ZONE OF USELESSNESS
web-adapter 0.10 0.90 0.00 on the main sequence
order-service 0.40 0.50 0.10 acceptablego deeper
Give the formula for A, state the main sequence line, and name the two bad corners with one example each.
Add D, the Stable Abstractions Principle, and the concrete refactorings that move a component toward the line.
Discuss volatility as the missing dimension, combine D with VCS churn, and cover the cost of speculative interfaces.
Position the metric inside architectural governance: sampled trends, fitness functions for specific rules, explicit language-convention caveats, and resistance to turning D into a target.
### Abstractness ``` A = Na / Nc Na = number of abstract classes + interfaces in the component Nc = total number of types in the component ``` "Abstract" means a type that cannot be instantiated and exists to be implemented/extended — an interface, abstract class, protocol, trait, or (in dynamic languages) a documented duck-typed contract. A = 0: purely concrete. A = 1: nothing but contracts. Why would abstractness matter? Because of the **Open–Closed Principle**: a component that others depend on should be extendable without being modified. Extension without modification requires abstractions to extend. So the more depended-upon a component is, the more of its public surface should be abstract. ### The main sequence Plot every component on a square with I on the x-axis and A on the y-axis. Two corners are trouble: - **(0, 0) — the Zone of Pain.** Maximally stable *and* maximally concrete. Everything depends on it, and there is no abstraction through which to vary it. Changing it is a system-wide edit; extending it is impossible without modification. Real examples: a concrete database schema, a concrete `Utils` class used by every module, a rigid serialized data format, a concrete framework base class. Note the nuance: some inhabitants of this zone are *fine* because they are **non-volatile** — a `String` type, a stable math library, a well-known standard library. Pain only bites when a zone-of-pain component actually changes. - **(1, 1) — the Zone of Uselessness.** Maximally abstract and maximally unstable: abstractions nobody depends on and (often) nobody implements. This is leftover speculative generality — interfaces created "in case we need another implementation" that never arrived. It is dead weight and should be deleted or inlined. The line joining (0, 1) and (1, 0) — that is, **A + I = 1** — is the **main sequence**. Points on it are balanced: fully stable components are fully abstract; fully unstable components are fully concrete; middling components are partly both. ### Distance ``` D = | A + I - 1 | (range 0..1, 0 = on the line) D' = | A + I - 1 | / sqrt(2) (true perpendicular distance, some tools use this) ``` A component with A = 0.0 and I = 0.1 has D = 0.9 — deep in the zone of pain. A component with A = 0.9, I = 0.9 has D = 0.8 — deep in uselessness. A component at A = 0.4, I = 0.7 has D = 0.1 — fine. Aggregate uses: mean D over the codebase as a rough architectural-health trend line; per-component D sorted descending as a refactoring backlog. ### Related principles - **Stable Abstractions Principle (SAP)**: *a component should be as abstract as it is stable.* This is literally the main sequence restated. Together with the Stable Dependencies Principle (depend toward lower I), SAP is the component-level restatement of the Dependency Inversion Principle: high-level policy is stable and abstract; low-level detail is unstable and concrete. ### How to move a component off the line — the actual refactorings - **Stuck at low A, low I (pain):** extract interfaces for the depended-upon behavior, move them into an abstraction component, and have dependents import the abstractions. Or accept it if the component is genuinely frozen. - **Stuck at high A, high I (uselessness):** delete the unused abstractions, or collapse the interface into its single implementation. "One interface, one implementation, forever" is the classic tell. - **Concrete component that everything depends on because it holds shared data structures:** consider whether the types belong to a shared kernel (accepted, low volatility) or whether each consumer should own its own model. ### Caveats you should voice in an interview 1. **Language dependence.** A is easy to compute in Java/C#/Kotlin; in Python, Go, JavaScript, or Rust the notion of "abstract type" maps imperfectly (Go interfaces are structural and usually declared by the *consumer*; Python has ABCs and Protocols; TypeScript interfaces vanish at runtime). Report the counting convention. 2. **Interfaces are not free.** Chasing A upward produces one-implementation interfaces, indirection, and harder navigation. D is a smell detector; near-zero D is not a goal. 3. **It ignores volatility.** The metric cannot tell an inert concrete component from a churning one. Combine D with change frequency from version-control history — high D **and** high churn is where to act. 4. **Goodhart's law.** The moment D becomes a KPI, teams add empty interfaces to game it.
- Can a component legitimately sit in the zone of pain?Yes, if it is non-volatile. Standard library types, a frozen shared value-type library, or a stable math kernel are concrete and heavily depended on but never change, so the rigidity costs nothing. Pair D with change-frequency data before acting.
- How do abstractness and instability relate to the Dependency Inversion Principle?They are its component-scale form. DIP says high-level policy should not depend on low-level detail but on abstractions; on the A/I map that means the stable end of every dependency chain is the abstract end, which is exactly the Stable Abstractions Principle and the main sequence.
- What is wrong with making D a team KPI?It is trivially gamed by adding interfaces with a single implementation, which increases indirection without decoupling anything. Use D to generate a candidate list for human review, and guard specific architectural rules with fitness functions instead.
Think of a power grid. High-voltage trunk lines (stable, everyone depends on them) are standardized to a published spec — an abstraction — so any generator or substation can plug in. The lamp on your desk (nothing depends on it) is fully concrete and can be replaced tonight. A concrete, one-off, non-standard trunk line that every substation is welded to is the zone of pain; a published connector spec that no device and no supplier ever uses is the zone of uselessness.
saying these in an interview costs you the question
- Claiming higher abstractness is always better — that manufactures the zone of uselessness
- Saying the zone of pain is always a defect, ignoring non-volatile components like standard libraries
- Mixing up the corners: pain is concrete+stable, uselessness is abstract+unstable
- Computing A from 'number of interfaces' without dividing by total types
- Treating D = 0 as a target to optimize instead of a smell to investigate