SOLID
SOLID names five heuristics for object-oriented design — Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion — assembled and taught by Robert C. Martin [1], [2]. The same body of work adds six packaging heuristics — Reuse/Release Equivalence, Common Closure, Common Reuse, Acyclic Dependencies, Stable Dependencies, Stable Abstractions — presented as the architecture-scale companion to the five [2].
The five have been criticised for thirty years as vague, overlapping, or arbitrary [3], [4], [5], [6], [7]. This page does not add another judgement of taste. It asks of each heuristic the questions an engineering discipline asks of any rule: what exactly does it assert, can a design be checked against it, and is the assertion true? It then asks the same of the set as a whole, because the heuristics are taught and applied together.
1 At a glance
| Heuristic | What it asserts | Status |
|---|---|---|
| SRP | a module has one reason to change | False: the adapter has two, under Martin’s own reading of his terms [8] |
| OCP | entities are open for extension, closed for modification | Vacuous: “extension” has no independent test |
| LSP | a subtype substitutes for its supertype | A definition of the subtype relation, not a design rule [9] |
| ISP | no client depends on methods it does not use | A corollary of DIP applied per client [10] |
| DIP | depend on abstractions; the client owns the interface | A placement directive with no criterion for which abstractions |
| REP, CCP, CRP, ADP, SDP, SAP | six constraints on package structure | Jointly unsatisfiable in general [11] |
2 Single Responsibility: false
The heuristic is stated as “a class should have only one reason to change” [1], and restated as a grouping rule: gather together the things that change for the same reasons, and separate those that change for different reasons [2]. Martin grounds the reason in people: a reason to change is a person or a group whose requirements can independently force a modification.
The adapter refutes the cardinality claim under that grounding. A Stripe payment adapter implements an internal payment port and calls Stripe’s API. Two groups can require it to change independently: the group that owns the internal port, and Stripe. The count of reasons to change is therefore two, and the adapter violates the heuristic. The counterexample is not marginal: every façade, bridge, API gateway, protocol translator, and anti-corruption layer that mediates between two independently governed interfaces has the same shape [8].
The prescribed remedy fails as well. Splitting the adapter is supposed to give each part one reason to change, but mediation requires both domains’ knowledge in the same reasoning step: a part that knows only one interface can no longer map between them. The paper examines the three natural salvage attempts — a data-transfer object, a mapping table, and delegation — and each either leaves the two domains’ knowledge in one module or destroys the mapping [8].
The grouping half miscounts too. Two stakeholders whose requests trace to one document force a false split; one stakeholder whose requests trace to two documents forces a false merge [8]. What survives is the grouping form re-grounded on documented sources of change — which is not Martin’s definition. The vocabulary is part of why the error survived: “concern” and “responsibility” carry a cardinality assumption into the sentence before any reasoning starts [12].
For practice, the consequence is direct: the heuristic demands a split that removes the element’s function, and a reviewer who invokes it cannot be satisfied without damaging the design.
3 Open/Closed: vacuous
The heuristic is stated as “software entities should be open for extension, but closed for modification” [1], [13].
The defect is in the word extension. Nothing fixes which changes count as extension and which as modification, and for any change both descriptions are available. Adding a branch to a conditional is a modification of the conditional; it is also an extension of the set of cases the conditional handles. Extracting a strategy is an extension of the entity’s behaviour; it is also a modification of the entity, which now delegates. A change to a configuration file modifies no code at all and can be described as an extension of the system’s behaviour.
The situation the heuristic addresses is real, and the mechanisms it points at are ordinary. A design must eventually serve requirements the team does not have yet, and the constructs that admit later code without editing earlier code are the ones every programmer knows: inheritance, composition, event handling, callbacks, higher-order functions. Each of them is a way of writing today’s code so that tomorrow’s code can attach to it. What the heuristic asks, however, is that this be done before the requirement exists — that the entity be made open for a change nobody has specified. That request cannot be met by method, because future domain knowledge is not available at design time. A designer either waits for the requirement and modifies the entity, or guesses; the heuristic treats the guess as compliance and the wait as violation, yet it supplies no test that separates a guess that will pay from one that will not.
So every design satisfies the heuristic under some description, and every design violates it under another. A rule with that property does not rank designs, because the description is chosen after the design and after the change. It cannot be checked in review — the reviewer and the author can always disagree about which description applies, and neither is wrong. It cannot be refuted: no program is a counterexample, because no program is required to be anything in particular. And it cannot decide a design question, including the question it appears to answer, which is when the cost of an extension point is justified [2].
What the heuristic names is a real observation — that some changes are cheap after a boundary exists and expensive before it, and that ordinary constructs can buy that cheapness. It converts the observation into no criterion: it licenses whatever was built, describing it afterwards as open or closed as needed, and it cannot say which of the available mechanisms should be put in place now.
4 Liskov Substitution: a definition
Liskov and Wing define the subtype relation behaviourally: S is a subtype of T if an object of type S can be used wherever an object of type T is expected, without altering the desirable properties of the program [9].
That sentence defines what “subtype” means. It is a criterion of correctness for the claim “S is a subtype of T”, not a prescription about which types should exist, which hierarchies are worth building, or where a boundary belongs.
Applying it classifies and stops. If a square cannot stand in for a mutable rectangle, then the square is not a subtype of the rectangle — the modelling claim is false, and the defect is in the claim, not in the definition. The definition does not say what to do next: remove the hierarchy, separate the types, or keep the hierarchy and change the invariant. That choice is a design decision the definition does not reach.
The example that made the rule famous — a square inheriting from a rectangle — is one instance of that classification, and no more. Type substitution fails when it is done incorrectly, and the square is the standard demonstration: a client that sets width and height independently receives a square it did not ask for. What the example does not show, and Martin does not say, is that the modelling choice behind it is contingent. In drawing software a square is commonly modelled as a shape with a single side length rather than as a rectangle under a constraint, because the operations that matter are move, resize, and rotate, and neither width nor height exists on its own. Modelled that way, the substitution question never arises. The definition cannot choose between the two models: both describe their objects correctly, and the choice turns on which operations the domain requires.
A definition is also not the kind of thing that can be falsified or improved by evidence, which is why the substitution rule is best read as the boundary of a vocabulary rather than as guidance. The actionable content usually attributed to it is contractual — preconditions, postconditions, invariants [13] — and that discipline checks a relation the designer has already chosen. It is not a criterion for choosing it.
5 Interface Segregation: a corollary
The heuristic says that no client should be forced to depend on methods it does not use [1].
DIP contains an ownership clause: the client owns the interface it depends upon [2]. Apply that clause once per client of a shared provider. Each client receives its own interface, the provider implements all of them, each client sees only its own slice, and no client depends on a method it does not use — which is the structure ISP prescribes. At the class level the two procedures produce the same classes, the same interfaces, and the same dependency and implementation relations, and the converse holds as well: any interface segregation is structurally indistinguishable from per-client application of the ownership clause [10].
ISP is therefore a corollary, not an independent fifth letter. Naming it has pedagogical value — it directs attention to the per-client reading of DIP, which the textbooks underemphasise — but it prescribes no design action that DIP does not already prescribe. The count of five overstates the independence of the set, and the acronym’s shape has been mistaken for its structure [10].
6 Dependency Inversion: a directive without a criterion
The heuristic says that high-level modules should not depend on low-level modules, that both should depend on abstractions, and that abstractions should not depend on details [1], [2]. Its ownership clause adds who writes the abstraction: the client.
What it fixes is the direction of a dependency that already exists. What it does not fix is which abstractions should exist, where a boundary belongs, or whether a given dependency is justified at all.
The gap is not a matter of interpretation. Pushed towards packaging, the heuristic admits three readings, and they disagree: a co-location reading, an examples reading, and an ownership reading [11]. The co-location reading contradicts Reuse/Release Equivalence; the examples reading contradicts Interface Segregation; the ownership reading, the only one that survives, places no constraint on package structure at all [11].
So the heuristic directs the arrow once the abstraction is chosen, and is silent on the choice. The choice is the design question.
7 ISP and DIP are conditional
These two are neither false nor vacuous, which is why they survive where the others do not. Interface segregation states a real constraint on who may see which operations, and dependency inversion states a real constraint on the direction of a dependency and on who owns the interface [2], [10].
They are wrong when applied blindly, because the structure they prescribe is right only when the two sides of the interface are governed independently. Where the client’s needs and the provider’s needs move together, a client-specific interface adds an indirection with no boundary behind it: the provider must still change when the client changes, and the interface is one more place to edit. Where they move apart, the indirection is the boundary, and the dependency points at the part that changes less. The heuristics name the structure but not the condition, which is whether the two sides are driven by independent change drivers [14]. Applied uniformly, they produce an interface for every use and an abstraction for every collaborator — indirection without a boundary.
That is the sense in which these two are conditionally true: their conclusion holds under a hypothesis neither of them states, and the hypothesis is a fact about the domain’s sources of change.
8 The packaging heuristics: jointly unsatisfiable
The six packaging heuristics are stated as follows [2]. Reuse/Release Equivalence (REP): the unit of reuse is the unit of release, so a package must be a viable unit of reuse for everyone who depends on it [2], [11]. Common Closure (CCP): elements that change for the same reasons belong in the same package. Common Reuse (CRP): elements used together belong in the same package. Acyclic Dependencies (ADP): the package dependency graph has no cycles. Stable Dependencies (SDP): depend in the direction of stability. Stable Abstractions (SAP): a stable package should be abstract.
Take the minimal configuration that DIP and ISP together endorse: one provider implementing two client-specific interfaces, serving two clients [11]. Fifty-two partitions of the five elements are possible, and the heuristics are supposed to narrow them. They do not narrow them to anything, because CCP and REP cannot both hold. CCP puts each client interface with its client, since they change for the same reasons. REP then condemns the result: the provider must implement both interfaces, so the provider’s package must depend on both client packages, including one whose elements it never uses [11].
The escape hatches do not escape. Martin presents CCP, CRP, and REP as a tension triangle in which the architect chooses a vertex to sacrifice; dropping REP or CRP does not restore satisfiability for this configuration, only dropping CCP produces any solutions at all, and then five partitions survive with no heuristic left to select among them [11]. The three natural placements — with the client, with the provider, in its own package — each have principled support and principled condemnation from inside the same corpus [11].
The consequence is stronger than “these heuristics conflict sometimes”. Every system with a provider serving two or more clients through client-specific interfaces violates at least one packaging heuristic at every point in its history. The notion of technical debt assumes a principled ideal that the debt departs from; for packaging, no such ideal exists [11].
9 Against the standards of engineering
Two standards define engineering practice: grounding in a theory or a model, and falsifiability [15], [16], [17], [18]. Both are absent here.
On grounding, the heuristics are selected, not derived. They respond to symptoms their authors observed, and no model of the domain, theory of change, or empirical result selects this set rather than another [3], [4], [5]. The corpus is presented as five rules, but the rules were developed separately, and their five-letter arrangement is an acronym rather than a structure [2], [3]. The packaging heuristics were added later, at a different scale, and are not consistent with the earlier set when both are pushed [11].
On falsifiability, the set splits into four kinds, and none of them is a tested engineering claim. SRP is false: it makes a countable assertion, and the count comes out wrong [8]. OCP is unfalsifiable: no observation can violate it, because the description of the change is chosen to suit the verdict. LSP is a definition: it fixes the meaning of a term, and definitions are not refuted by evidence [9]. ISP is a corollary of DIP: it follows by derivation and predicts nothing that DIP does not [10]. DIP is underdetermined: it constrains a direction, not a structure [11]. The packaging heuristics are jointly contradictory: there are configurations for which no assignment satisfies them [11].
10 The missing criterion: the domain’s change structure
Each heuristic classifies by something internal to the code: a responsibility, an extension point, a type relation, an interface shape, a dependency direction, a package metric. None of them classifies by the domain’s sources of change.
Those sources are external and independent. A regulator, a payment provider’s API, a customer segment, or an accounting standard each force changes on its own schedule, and their reach cuts across the categories the heuristics use [14]. A boundary drawn by technical role therefore cuts through a source of change: one required change then reaches several modules, and each module mixes pieces that change for unrelated reasons. A boundary drawn to keep a source of change whole would avoid that, and none of the heuristics can express the difference, because none of them has a term for an external source of change.
That is the missing criterion, and it is why the heuristics cannot answer the question they are consulted for. They can report a symptom — two things in one class, a dependency pointing down, a package that is hard to reuse — but a symptom is not a boundary rule. Two decompositions can score identically on every heuristic and differ completely in how much work one change costs.
11 What remains
Not nothing. The substitution contract is sound as a definition of subtyping and useful as a check on a hierarchy the designer has already chosen [9]. Interface segregation is sound as the per-client application of interface ownership [10]. Dependency inversion is sound as a statement about the direction of a chosen dependency, and its ownership clause says who should write the interface [2]. Acyclicity is a structural property worth enforcing, and the observation that some changes are cheap after a boundary exists is real. The SRP grouping form also survives in a corrected version — grouping by documented sources of change — which is not the heuristic as stated [8].
What does not survive is the claim that these are a method. One is false, one is vacuous, one is a definition, one is a corollary, one is a directive without a criterion, and the packaging set is unsatisfiable as a set. What is left is practitioner advice: memorable names for symptoms, with no model that selects them, no test that refutes them, and no procedure that decides a boundary [7]. That is a legitimate thing to write and to teach. It is not engineering, and it should not be presented as the discipline’s foundation.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- Why SRP Is Wrong: The Cardinality Error in the Single Responsibility Principle — Zenodo, 2026 — doi:10.5281/zenodo.20415656
- The Interface Segregation Principle Is a Corollary of the Dependency Inversion Principle: A Structural Proof — Zenodo, 2026 — doi:10.5281/zenodo.19633560
- SOLID: Where Do Client-Owned Interfaces Live? DIP, ISP, and Packaging Principles — Zenodo, 2026 — doi:10.5281/zenodo.19636635
- SRP’s Vocabulary Problem: Why Every Reformulation Failed — Zenodo, 2026 — doi:10.5281/zenodo.20445691
- The Change-Driver Taxonomy — Zenodo, 2026 — doi:10.5281/zenodo.21669704