Liskov Substitution Principle¶
Certify that a subtype can safely replace its supertype only when it honours the supertype's full contract toward clients — weakening preconditions, strengthening postconditions, preserving invariants — regardless of taxonomy or compilability.
Core Idea¶
The Liskov Substitution Principle (LSP; Barbara Liskov 1987, formalised with Jeannette Wing in 1994) states the contractual condition under which a subtype can correctly replace its supertype in a typed object-oriented program: if S is a declared subtype of T, then substituting an instance of S for an instance of T must leave the behaviour of any client program written against T's interface unchanged. The precise behavioural conditions are: preconditions may be weakened but not strengthened (the subtype must accept at least the inputs the supertype accepted), postconditions may be strengthened but not weakened (the subtype must guarantee at least what the supertype guaranteed), invariants of the supertype must be preserved in the subtype, and exception behaviour must not surprise the client.
The principle separates three things that object-oriented design conflates: syntactic subtyping (the subtype possesses the supertype's methods), taxonomic is-a (ordinary-language usage treats the subtype as a kind of the supertype), and behavioural subtyping (the subtype honours the supertype's contract toward clients). The classic demonstration is the rectangle–square hierarchy: a square is-a rectangle in geometry and satisfies the syntactic interface, but cannot be a behavioural subtype of a mutable rectangle whose contract specifies that width and height are independently settable — because setting the width of a square must also set the height, violating the postcondition that the other dimension is unchanged. Client code that passes a square where a mutable rectangle is expected and then checks independent dimension mutability will break. The lesson is that inheritance should be used only when the subtype can honour the supertype's full behavioural contract, not merely when ordinary-language taxonomy suggests a kind-of relationship.
LSP is the L in the SOLID design principles and grounds the practical interventions of preferring composition over inheritance, interface-based hierarchies over implementation inheritance, and design-by-contract assertions (formalised in Bertrand Meyer's Eiffel and in Java's JML) that make the contract explicit enough to check. Broken inheritance hierarchies that violate LSP are a documented source of maintenance failures in large OO codebases: client code becomes fragile when substituting subtypes silently changes behaviour in ways the contract did not specify.
Structural Signature¶
Sig role-phrases:
- the supertype contract — the preconditions, postconditions, invariants, and exception behaviour a type T promises to its clients
- the declared subtype — a type S syntactically inheriting or implementing T's interface, the candidate stand-in
- the client written against T — the code depending on T's contract, whose behaviour must remain unchanged under substitution
- the four variance conditions (the engineered guarantee) — substitution is sound iff S weakens (not strengthens) preconditions, strengthens (not weakens) postconditions, preserves T's invariants, and does not surprise on exceptions — contravariance on inputs, covariance on outputs
- the three-way disambiguation — the load-bearing separation of syntactic subtyping (has the methods) and taxonomic is-a (ordinary-language kind-of), both set aside as non-determinative, from behavioural subtyping, the only one checked
- the interface-level verdict — passing all four certifies substitutability across every client at once, with no need to enumerate call sites; failing any one is unsound
- the violation reclassification (what it deliberately discards) — a silent behaviour change on substitution is not a style blemish but a type-system bug the compiler cannot catch, because it lives in behaviour the signature does not express (rectangle–square against a mutable rectangle)
- the fixed remedy branch — drop the inheritance, restructure around interfaces, prefer composition, or weaken the supertype's contract until the subtype can honour it; design-by-contract assertions make the contract mechanically checkable
What It Is Not¶
- Not satisfied by syntactic subtyping. Possessing the supertype's methods and compiling in its place is syntactic conformance, which the type checker verifies; LSP demands behavioural conformance — honouring the supertype's preconditions, postconditions, invariants, and exception behaviour toward clients. A subtype can pass the compiler and still violate LSP, because the breach lives in behaviour the signature does not express.
- Not implied by taxonomic "is-a." That ordinary language calls S a kind of T — "a square is a rectangle" — does not make S a behavioural subtype. The square satisfies the geometric taxonomy yet fails LSP against a mutable rectangle whose contract promises independently settable dimensions. The right question is not "is S a kind of T?" but "can S honour every guarantee T made to T's clients?"
- Not a style guideline. A subtype that silently changes behaviour on substitution is not a code smell to be tidied when convenient; it is a type-system bug — a defect in the design's contracts that the compiler cannot catch and that makes client code fragile. Treating it as mere untidiness misses why misused inheritance is a documented source of maintenance failure.
- Not "preconditions may be strengthened." The variance is counterintuitive and routinely inverted: a subtype may only weaken preconditions (accept at least what the supertype accepted) and only strengthen postconditions (guarantee at least what it guaranteed). Strengthening a precondition — demanding more of inputs than the supertype did — breaks clients that legitimately relied on the looser requirement.
- Not a constraint on inheritance as a mechanism. LSP does not govern how inheritance works syntactically; it is the semantic contract that determines which inheritance relationships are sound. It is equally distinct from encapsulation (hiding internal structure) and interface segregation (not forcing clients to depend on unused methods) — it concerns behavioural conformance of a subtype to its supertype's external contract.
Scope of Application¶
The Liskov Substitution Principle lives within typed object-oriented software — across the design and tooling practices that govern a nominal type hierarchy with client code written against a supertype's contract; its reach is bounded there, the four-condition variance check carrying intact as the hierarchy changes. (The general behavioral-substitutability pattern recurs cross-domain — contract novation, ecological function replacement, organizational role substitution — but each supplies its own contract and enforcement and belongs to that pattern, not to the SOLID principle.)
- OO design and type systems — the foundational constraint on inheritance hierarchies, informing class libraries, frameworks, and inheritance trees, and the L of the SOLID principles.
- Design-by-contract programming — Meyer's Eiffel formalises the pre/post/invariant conditions, with LSP the soundness check on whether a class hierarchy is contractually valid.
- Static analysis and formal verification — variance analysis, refinement-type checkers, and JML for Java automate the behavioural-subtyping check the principle specifies.
- Refactoring practice — "favor composition over inheritance" and "tell, don't ask" guidelines target the LSP failures common in deep inheritance trees.
- API and library design — framework authors must document the contract their subclassable types impose, and downstream extenders must respect it for client code to stay valid under substitution.
Clarity¶
LSP's clarifying force is that it cleanly separates three notions of subtyping that object-oriented design routinely collapses into a single intuition that one class "is a kind of" another. Syntactic subtyping asks only whether the subtype possesses the supertype's methods — whether it compiles in the supertype's place. Taxonomic is-a is the ordinary-language judgement that the subtype names a kind of the supertype, the judgement that makes "a square is a rectangle" feel obviously true. Behavioural subtyping — the only one LSP cares about — asks whether the subtype honours the supertype's full contract toward clients: its preconditions, postconditions, invariants, and exception behaviour. The principle's contribution is to name the gap between these and to supply a precise contractual check for the third, so a designer stops inferring substitutability from either compilability or taxonomy. The rectangle-square case becomes legible exactly here: the square passes the syntactic test and satisfies the geometric taxonomy, yet fails behavioural subtyping against a mutable rectangle whose contract promises independently settable dimensions. The sharp question LSP lets a designer ask is therefore not "is S a kind of T?" but "can S honour every guarantee T made to T's clients?" — and the variance rules (weaken preconditions, strengthen postconditions, preserve invariants) make that question mechanically checkable rather than a matter of taste.
The second clarity is a reclassification of what a broken inheritance hierarchy is. Without LSP, a subtype that silently changes behaviour when substituted reads as a style problem — inelegant inheritance, a code smell to be cleaned up when convenient. LSP reframes it as a type-system bug: a defect in the design's contracts that will make client code fragile in ways the compiler cannot catch, because the violation lives in behaviour the type signature does not express. That reframing changes the remedy from cosmetic to structural — drop the inheritance, restructure the hierarchy around interfaces, prefer composition, or weaken the supertype's contract until the subtype can honour it — and explains why the misuse of inheritance is a documented source of maintenance failure rather than mere untidiness. By making the supertype's contract the thing inheritance must respect, LSP turns "should this be a subclass?" from an appeal to intuition into a contractual test with a definite answer.
Manages Complexity¶
Deciding whether one class may safely inherit from another is, without a principle to organise it, an open-ended judgement that pulls in the full semantics of both types, every client that might consume them, and the designer's intuitions about what "is a kind of" should mean. LSP compresses that judgement into a single, mechanically checkable contractual test. By replacing the question "is S a kind of T?" with "can S honour every guarantee T made to T's clients?", the principle collapses the whole sprawl of possible behavioural interactions between a subtype and the programs that use it into four variance conditions an analyst checks at the interface: preconditions may be weakened but not strengthened, postconditions may be strengthened but not weakened, the supertype's invariants must be preserved, and exception behaviour must not surprise the client. Satisfy all four and substitution is sound across every client written against T, with no need to enumerate those clients or trace each call site; violate any one and the substitution is unsound — the qualitative answer reads off the four-way check rather than from case-by-case inspection of program behaviour. The rectangle-square hierarchy is resolved this way in a single step: the square satisfies the syntactic interface and the geometric taxonomy yet fails the postcondition that a mutable rectangle's dimensions are independently settable, so the verdict (not a valid behavioural subtype of the mutable rectangle) falls out of the check without simulating client code.
The compression has a second face that tames a different sprawl — the open-ended question of what to do about a problematic hierarchy. LSP first disentangles three notions the bare intuition of "is-a" fuses — syntactic subtyping (has the methods), taxonomic is-a (ordinary-language kind-of), and behavioural subtyping (honours the contract) — so that the only load-bearing one, behavioural subtyping, is isolated and the other two are set aside as non-determinative. That single separation prevents the designer from inferring substitutability from compilability or from taxonomy, the two most common wrong routes. It then reclassifies a violation: a subtype that silently changes behaviour on substitution is not a style blemish to be tidied when convenient but a type-system bug — a contract defect the compiler cannot catch because it lives in behaviour the signature does not express — which fixes the remedy in advance to a small, structural menu (drop the inheritance, restructure around interfaces, prefer composition, or weaken the supertype's contract until the subtype can honour it) rather than an open field of cosmetic options. The high-dimensional question should this be a subclass, and if not what do we do? reduces to a four-condition contractual check plus a fixed branch of structural remedies, replacing taste and taxonomy with a test that has a definite answer.
Abstract Reasoning¶
The signature move is a four-condition variance check at the interface that decides substitutability without ever simulating client code. Asking whether a subtype S may stand in for a supertype T, the designer reasons not about the two classes' full semantics but about four contractual conditions: may S's preconditions only weaken (accept at least what T accepted)? may its postconditions only strengthen (guarantee at least what T guaranteed)? are T's invariants preserved? does S's exception behaviour avoid surprising the client? The characteristic inference runs from "all four hold" to "substitution is sound across every client written against T, with no need to enumerate those clients or trace their call sites," and from "any one fails" to "the substitution is unsound." The move replaces an open-ended judgement that would pull in both types' semantics and every consumer with a mechanical check whose verdict reads off the contract directly — the rectangle-square case resolved in one step because setting a square's width also sets its height, violating the mutable rectangle's postcondition that the other dimension is unchanged, so the verdict falls out without running client code.
This rests on a three-way disambiguation the designer must perform before trusting any "is-a" intuition: separating syntactic subtyping (S has T's methods, compiles in T's place), taxonomic is-a (ordinary language calls S a kind of T), and behavioural subtyping (S honours T's full contract toward clients). The move isolates the third as the only load-bearing one and sets the other two aside as non-determinative, so the inference runs from "S compiles where T is expected" or "S is a kind of T in ordinary language" not to "S is substitutable" but to the separate behavioural question. The discipline this imposes is refusing to infer substitutability from compilability or from taxonomy — the two most common wrong routes — and reframing the design question from "is S a kind of T?" to "can S honour every guarantee T made to T's clients?"
The decisive reclassification move changes what a substitution failure is, and thereby what to do about it: a subtype that silently alters behaviour when substituted is inferred to be not a style blemish to be tidied when convenient but a type-system bug — a contract defect the compiler cannot catch because the violation lives in behaviour the type signature does not express. The inference runs from "the type checker passes yet client code will break on substitution" to "this is a latent defect in the design's contracts, invisible to static typing," which is the move that explains why misused inheritance is a documented source of maintenance failure rather than mere untidiness. Because the violation is structural, the remedy is fixed in advance to a small branch rather than an open field of cosmetic options: drop the inheritance, restructure the hierarchy around interfaces, prefer composition over implementation inheritance, or weaken the supertype's contract until the subtype can honour it — the analyst infers which remedy from which of the four conditions failed and whether the supertype's promise or the subtype's behaviour is the negotiable one.
A final interventionist move runs at design time and predicts fragility before it manifests: reasoning forward from a contemplated inheritance relationship to whether the subtype can honour the full contract, the designer pre-empts the brittle hierarchy by making the supertype's contract explicit enough to check — design-by-contract assertions, refinement-type or behavioural-subtyping analyzers that operationalize the variance rules. The inference runs from "if S strengthens a precondition or weakens a postcondition, some client written against T will break" forward to a design that either forbids the inheritance or encodes the contract so the violation is caught mechanically — converting "should this be a subclass?" from an appeal to taste into a checkable prediction about client breakage.
Knowledge Transfer¶
Within typed object-oriented software the principle transfers as mechanism, and across the practice its formal content carries intact. It is the foundational constraint on inheritance hierarchies in OO design and type systems (informing class libraries, frameworks, and inheritance trees); it is operationalized in design-by-contract programming (Meyer's Eiffel formalizing pre/post/invariant conditions, with LSP the soundness check on a hierarchy); it is automated in static analysis and formal verification (variance analysis, refinement-type checkers, JML for Java); it motivates refactoring guidelines ("favor composition over inheritance," "tell, don't ask") that target the LSP failures common in deep inheritance trees; and it governs API and library design, where framework authors must document the contract their subclassable types impose and downstream extenders must respect it for client code to stay valid. Across these the transfer is literal because the substrate is the same — a nominal type hierarchy with client code written against a supertype's contract — so the whole apparatus carries: the four-condition variance check (weaken preconditions, strengthen postconditions, preserve invariants, do not surprise on exceptions), the three-way disambiguation of syntactic versus taxonomic versus behavioural subtyping, the reclassification of a violation as a type-system bug rather than a style blemish, and the fixed remedy branch (drop the inheritance, restructure around interfaces, prefer composition, or weaken the supertype's contract). The vocabulary travels because it is OO vocabulary — subtype, supertype, contract, precondition, postcondition, invariant, variance — and what moves is not an analogy to type design but type design itself, applied to a different hierarchy.
Beyond typed software the transfer is a shared abstract mechanism carried by a more general principle, not by the named one. Strip the OO apparatus and the portable skeleton is a specialization may stand in for its generalization only if it honours the contract the generalization fulfilled toward outside clients — not strengthening what was required of inputs nor weakening what was guaranteed on outputs. That behavioral-substitutability pattern (an emergent candidate in its own right) genuinely recurs across substrates: in legal-contract novation, where an assignee must satisfy at least the obligations the original obligor accepted; in ecological function replacement, where a "replacement" species fills a niche only if it satisfies the original's role; and in organizational role substitution, where a deputy can stand in for a director only if the director's external commitments are honoured. When the cross-domain lesson is wanted — a stand-in is safe exactly when it preserves every promise the thing it replaces made to those depending on it — it is that general substitutability principle that carries it, with LSP as one canonical illustration. The home-bound cargo is the entire typed-OO formal apparatus that makes LSP mechanically checkable: the precise pre/post/invariant variance rules, the history constraints, the exception-behaviour conformance, the contravariance-on-inputs/covariance-on-outputs signature reasoning, and LSP's place as the L of SOLID — none of which survives extraction, since each other domain has its own independently developed enforcement (novation formalities, ecological function tests, succession rehearsals) rather than method-signature variance. So importing LSP's apparatus into contract law or ecology is analogy: those domains share the substitutability shape and drop nearly all the formal machinery, retaining only the generic "a special case must honour the general case's contract" slogan. The disciplined position is that LSP transfers across software practice as genuine shared machinery, while its cross-domain reach belongs to the behavioral-substitutability pattern it instantiates — better named at that general level, with each substrate supplying its own contract and its own enforcement, than imported under the software-specific name (see Structural Core vs. Domain Accent).
Examples¶
Canonical¶
The defining case is the mutable rectangle and square. Define Rectangle with independent setters and an area(), whose contract promises that width and height are settable independently. A client relies on exactly that: void resizeCheck(Rectangle r){ r.setWidth(5); r.setHeight(4); assert r.area() == 20; }. Now derive Square extends Rectangle, overriding setWidth to also set the height (and vice versa) to preserve squareness. Substitute a Square: setWidth(5) makes it 5×5, then setHeight(4) makes it 4×4, so area() returns 16, not 20 — the assertion fails. The Square compiles perfectly and is a square-is-a-rectangle in geometry, yet it breaks a client written against the mutable rectangle.
Mapped back: The independent-dimensions promise is the supertype contract; Square is the declared subtype; resizeCheck is the client written against T. Overriding the setters weakens the postcondition that the other dimension is unchanged — a failure of the four variance conditions — so the interface-level verdict is "not a behavioural subtype." That it compiles and fits the taxonomy yet breaks is the three-way disambiguation: syntactic and taxonomic is-a set aside, behavioural subtyping the only test.
Applied / In Practice¶
A live instance ships in the Java standard library. java.util.List declares an add(E) method as part of its contract — callers written against List may reasonably expect to append. But Arrays.asList(...) returns a fixed-size list, and Collections.unmodifiableList(...) an immutable view; both implement List yet throw UnsupportedOperationException from add. A method written generically against List that appends will compile and run against an ArrayList but throw at runtime when handed one of these. This is a documented substitution hazard: the "subtype" strengthens the precondition (demanding the caller never mutate), which no client of the general List contract was required to honour.
Mapped back: List's mutating operations are the supertype contract; the fixed-size/immutable implementations are declared subtypes; generic list-appending code is the client written against T. Throwing on add strengthens a precondition — the forbidden direction in the four variance conditions — a violation reclassification as a type-system bug the compiler cannot catch, since the signature still advertises add. The fixed remedy branch appears in practice as splitting read-only and mutable interfaces so the contract matches behaviour.
Structural Tensions¶
T1: Behavioural conformance versus what the type checker can enforce (a bug the compiler cannot catch by construction). LSP demands behavioural subtyping — honouring preconditions, postconditions, invariants, and exception behaviour — yet static typing verifies only syntactic conformance. The violation, by definition, lives in behaviour the signature does not express, so the principle names a defect the type system cannot detect without additional machinery: design-by-contract assertions, refinement types, JML. The rigor of the principle therefore outruns the mechanization ordinarily available, and full behavioural-subtyping verification is in general undecidable. The tension is that LSP presents as a "mechanically checkable" contractual test, but the checkability is real only to the extent the contract has been made explicit enough for a tool to see — and most codebases carry the contract in prose, tests, and habit rather than in checkable assertions. The check is mechanical in principle and manual in practice. Diagnostic: Is the supertype's contract encoded where a tool (DbC, refinement types) can verify the variance conditions, or does LSP conformance here rest on human judgment the compiler cannot back?
T2: Contract fidelity versus inheritance's reuse value (the discipline that restricts the mechanism it governs). Inheritance exists to buy code reuse and polymorphism cheaply; LSP restricts when that purchase is legitimate, forbidding hierarchies that ordinary taxonomy invites (square from mutable rectangle) and pushing designers toward composition and interface segregation. Honouring the principle strictly narrows what inheritance may express, and "favor composition over inheritance" trades the polymorphic convenience of a subclass for the verbosity of delegation. The tension is that the constraint keeping hierarchies sound also blunts the expressiveness that made inheritance attractive: a codebase that obeys LSP maximally may have almost no implementation inheritance left. Too lax and hierarchies become fragile; too strict and the language's central abstraction mechanism goes largely unused. Diagnostic: Is inheritance being used here for genuine behavioural substitutability, or for reuse convenience that composition would serve without incurring the LSP obligation?
T3: A strong supertype contract versus a subtypable one (soundness can always be bought by promising less). The remedy branch includes "weaken the supertype's contract until the subtype can honour it" — and this always works, because a contract that guarantees less is easier to satisfy. But weakening the supertype's promise weakens what every client receives: an immutable-friendly rectangle that no longer guarantees independently settable dimensions admits Square, at the cost of the very guarantee that made the mutable rectangle useful. There is a standing pull between a strong contract (valuable to clients, hard to subtype) and a weak contract (easy to subtype, thin for clients). LSP tells you the substitution is unsound but not which side should give — and the cheap fix, weakening the supertype, silently degrades the abstraction for everyone to accommodate one subtype. Diagnostic: Should this be resolved by rejecting the subtype (preserving a strong contract clients rely on) or by weakening the supertype's contract (admitting the subtype at the cost of thinning every client's guarantee)?
T4: Presupposing a full explicit contract versus contracts that are implicit and underspecified (the test needs a specification the code rarely has). The four-condition check presupposes the supertype's full contract — preconditions, postconditions, invariants, exception behaviour — is known. In practice contracts are largely implicit: List.add advertises a method signature, but "callers may reasonably expect to append" is a convention, not a written postcondition, and whether Arrays.asList violates LSP turns on a contract nobody formally stated. So the verdict of soundness or violation depends on a specification that frequently does not exist, and reasonable engineers disagree about what the supertype actually promised. The principle that replaces taste with a test quietly reintroduces judgment at the step of deciding what the contract is. The mechanical check is only as determinate as the contract feeding it, and the contract is usually the underdetermined part. Diagnostic: Is the guarantee the subtype allegedly breaks a stated part of the supertype's contract, or an unwritten expectation whose status as "the contract" is itself in dispute?
T5: Soundness against every possible client versus the clients that realistically exist (universality bought with pathological cases). The interface-level verdict certifies substitutability across every client written to T's full contract, with no need to enumerate call sites — its great economy. But "every possible client" includes ones that exercise behaviour no real consumer uses: the mutable-rectangle case breaks only a client that sets width and height and then asserts their independence, which few programs actually do. Strict LSP therefore condemns hierarchies whose only failing clients are contrived, while a pass is only as safe as the assumption that all clients stay within the formal contract (real clients sometimes depend on undocumented behaviour a "sound" subtype changes). The universality that lets the check ignore call sites also makes it indifferent to which breakages matter. The tension is between a guarantee quantified over all conceivable clients and the pragmatic question of whether any client that exists will break. Diagnostic: Does the substitution break a client that plausibly exists, or only a pathological consumer of the full contract — and are any real clients depending on behaviour outside the contract that a "sound" subtype would still alter?
T6: Autonomy versus reduction (a named SOLID principle or the software instance of behavioural substitutability). LSP is a fully specified typed-OO principle with irreducibly local apparatus — contravariance on inputs and covariance on outputs, history constraints, exception-behaviour conformance, design-by-contract enforcement, its place as the L of SOLID — and within software it transfers as literal machinery across type systems, DbC, static verification, refactoring, and API design, because the substrate is the same nominal hierarchy everywhere. But beyond typed software it does not travel as mechanism: the portable skeleton — a specialization may stand in for a generalization only if it honours the contract the generalization fulfilled toward outside clients — is the general behavioral_substitutability pattern (an emergent candidate), recurring in contract novation, ecological function replacement, and organizational role substitution, each with its own enforcement rather than method-signature variance. Calling novation "LSP" is analogy that keeps only the slogan. The tension is between a principle that earns its own formal apparatus and the recognition that its cross-domain lesson belongs to the substitutability pattern it instantiates. Diagnostic: Resolve toward the general behavioural-substitutability pattern when the point is a stand-in preserving promises to dependents outside software; toward named LSP when certifying a subtype against a supertype's contract in a typed hierarchy.
Structural–Framed Character¶
The Liskov Substitution Principle sits at the framed-leaning position on the structural–framed spectrum. Unlike the evaluatively neutral mechanisms nearby, LSP carries real evaluative weight, and that is the first mark pointing framed: to call a hierarchy "not a behavioural subtype" or a silent behaviour-change "a type-system bug" is to render a verdict — a finding that a design is unsound, that inheritance was misused — not a neutral description of a mechanism running in the world. The verdict is engineering-normative (a correctness judgment) rather than moral, which keeps it off the framed pole, but it convicts all the same, and its whole point is to reclassify a "style blemish" as a defect. Human_practice_bound is high: the concept is constituted by the practice of typed object-oriented programming and dissolves the instant that practice is removed — a supertype, a declared subtype, a client written against a contract, and the four variance conditions are all artifacts of a designed type system, with no observer-free existence. Institutional_origin is pronounced: this is a named principle of a specific engineering tradition — Liskov 1987, formalized with Wing 1994, the L of the SOLID acronym, wired into Meyer's design-by-contract lineage — a distinction drawn inside software methodology, not a fact of nature. Vocab_travels is low: subtype, supertype, precondition/postcondition variance, invariant, contravariance-on-inputs/covariance-on-outputs are OO type-theory terms that lose their referents off that substrate. On import_vs_recognize the pattern is bimodal but tips framed at the boundary that matters: within typed software the mechanism is recognized intact across type systems, DbC, static verification, refactoring, and API design, but beyond it — contract novation, ecological function replacement, organizational role substitution — "LSP" moves only by analogy, keeping the slogan while each domain supplies its own enforcement in place of method-signature variance.
The one genuinely structural feature is the portable skeleton the entry names behavioral_substitutability: a specialization may stand in for its generalization only if it honours the contract the generalization fulfilled toward outside clients — not strengthening what was required of inputs, not weakening what was guaranteed on outputs. That skeleton is substrate-portable and recurs as mechanism wherever a stand-in must preserve promises to dependents, which is exactly what tempts a structural reading. But it does not pull LSP off the framed side, because that portable structure is precisely what LSP instantiates from its umbrella, not what makes "LSP" itself travel: the cross-domain reach belongs to the general substitutability pattern, while the principle's distinctive content — the four-condition variance check, the syntactic/taxonomic/behavioural three-way disambiguation, the reclassification of a violation as a compiler-invisible type-system bug, the design-by-contract enforcement, and the fixed remedy branch — is exactly the part that stays home. Its character: an engineering-normative, practice-constituted soundness verdict whose every distinctive feature is typed-OO apparatus, structural only in the contract-preserving-substitution skeleton it borrows from its umbrella and frames as a correctness test.
Structural Core vs. Domain Accent¶
This section decides why the Liskov Substitution Principle is a domain-specific abstraction and not a prime, and it carries the case for its domain-specificity in the same breath — so it is worth being exact about what could lift and what stays home.
What is skeletal (could lift toward a cross-domain prime). Strip the typed-OO apparatus and a thin relational structure survives: a specialization may stand in for its generalization only if it honours the contract the generalization fulfilled toward outside clients — accepting at least what was required of inputs and guaranteeing at least what was promised on outputs. The portable pieces are abstract: a general thing with obligations to dependents, a candidate stand-in, and a directional contract test on inputs-and-outputs that certifies the swap without inspecting each dependent individually. That skeleton is genuinely substrate-portable — which is exactly why the entry names the general behavioral_substitutability pattern as the parent it instantiates, and why the same shape recurs in legal-contract novation, ecological function replacement, and organizational role substitution. But this contract-preserving-substitution skeleton is the core LSP shares with those, not what makes it the distinctive SOLID principle it is.
What is domain-bound. Almost everything that makes the concept LSP in particular is typed-OO furniture, and none of it survives extraction intact. The supertype contract stated as preconditions, postconditions, invariants, and exception behaviour; the four variance conditions — contravariance on inputs (weaken preconditions), covariance on outputs (strengthen postconditions), preserved invariants, no surprise on exceptions; the three-way disambiguation of syntactic subtyping, taxonomic is-a, and behavioural subtyping; the interface-level verdict that certifies across every client at once; the reclassification of a violation as a compiler-invisible type-system bug; the design-by-contract enforcement (Meyer's Eiffel, JML) and LSP's place as the L of SOLID — these are the worked apparatus. The decisive test: remove the nominal type hierarchy and its clients written against a supertype's contract and the four-condition variance check has nothing to bite on — "a special case must honour the general case's contract" collapses into a slogan, with none of the mechanically checkable machinery (signature variance, history constraints, exception conformance) that distinguishes an LSP verdict from any other appeal to "honour the original's promises."
Why this does not clear the prime bar. A prime is a relational structure whose vocabulary travels and whose cross-domain transfer is recognition of the same mechanism, not analogy. LSP's transfer is bimodal. Within typed object-oriented software the mechanism travels intact — across type systems, design-by-contract programming, static analysis and formal verification, refactoring practice, and API design — because the substrate is everywhere the same nominal hierarchy with clients written against a supertype's contract, so the four-condition check, the three-way disambiguation, the type-system-bug reclassification, and the fixed remedy branch all carry with their full content: this is type design applied to a different hierarchy, not an analogy to it. Beyond typed software it moves only by analogy: importing LSP's apparatus into contract law or ecology keeps only the substitutability shape and the "a special case must honour the general case's contract" slogan while dropping the pre/post/invariant variance rules, the signature reasoning, and the compiler-invisible-bug framing that are its substance — and each of those domains supplies its own independently developed enforcement (novation formalities, ecological function tests, succession rehearsals) rather than method-signature variance. And when the bare cross-domain lesson is wanted — a stand-in is safe exactly when it preserves every promise the thing it replaces made to those depending on it — it is already carried, in more general form, by the behavioral_substitutability pattern LSP instantiates. The cross-domain reach belongs to that pattern; "LSP," as named, carries typed-OO baggage that should stay home.
Relationships to Other Abstractions¶
Current abstraction Liskov Substitution Principle Domain-specific
Parents (1) — more general patterns this builds on
-
Liskov Substitution Principle is a decomposition of Substitutability Prime
LSP is behavioral substitutability specialized to typed inheritance and made checkable through contract variance rules.A subtype may replace a supertype only if client-visible function remains invariant under the swap. Weaker preconditions, stronger postconditions, preserved invariants, and compatible exceptions are the typed-OO apparatus that certifies the general replacement property.
Children (1) — more specific cases that build on this
-
Refused Bequest Domain-specific presupposes Liskov Substitution Principle
Refused Bequest presupposes LSP because the smell is precisely a subclass refusing inherited obligations that substitutability requires it to honor.The subclass accepts an inheritance edge yet stubs, throws from, or silently violates some inherited operation. That conduct is only diagnosable as refused bequest relative to the supertype-client contract and LSP's requirement that every promised behavior survive substitution.
Hierarchy paths (8) — routes to 4 parentless roots
- Liskov Substitution Principle → Substitutability → Compatibility
- Liskov Substitution Principle → Substitutability → Modularity → Decomposition
- Liskov Substitution Principle → Substitutability → Abstract Data Type → Information Hiding → Abstraction
- Liskov Substitution Principle → Substitutability → Containerization → Information Hiding → Abstraction
- Liskov Substitution Principle → Substitutability → Abstract Data Type → Information Hiding → Boundary
- Liskov Substitution Principle → Substitutability → Abstract Data Type → Interface → Boundary
- Liskov Substitution Principle → Substitutability → Containerization → Information Hiding → Boundary
- Liskov Substitution Principle → Substitutability → Containerization → Interface → Boundary
Not to Be Confused With¶
-
Syntactic / structural (duck) subtyping. The compiler-checkable notion that a type possesses the required methods and compiles in another's place — "if it has the interface, it fits." LSP is the strictly stronger behavioural condition: a type can pass the syntactic check and still violate LSP, because the breach lives in behaviour (preconditions, postconditions, invariants) the signature does not express. Tell: does the check stop at "does S have T's methods and compile?" (syntactic subtyping), or does it ask "does S honour every guarantee T made to T's clients?" (LSP)?
-
Design by contract. Meyer's broader methodology of specifying preconditions, postconditions, and invariants on operations and enforcing them with runtime assertions. LSP is not that methodology but the specific soundness condition on inheritance stated in its vocabulary — the rule that a subtype's contract must vary correctly (weaken preconditions, strengthen postconditions, preserve invariants) relative to its supertype's. Tell: is the topic how to write and check a single class's contract (design by contract), or whether a subtype's contract legitimately refines its supertype's so substitution is safe (LSP)?
-
Covariance and contravariance (type-theoretic variance). The signature-level typing rules for how parameter and return types may vary in subtypes — contravariant inputs, covariant outputs — enforced by the type checker. LSP is the behavioural principle those rules partially approximate: it governs the full contract (values, invariants, exceptions, side-effects), not just the types in the signature, and catches violations (the mutable square) that variance-correct signatures still permit. Tell: is the constraint purely on the types in the method signature (variance), or on the runtime behaviour promised to clients beyond what signatures encode (LSP)?
-
Interface Segregation Principle (ISP). The sibling SOLID principle that clients should not be forced to depend on methods they do not use, so fat interfaces should be split into focused ones. It concerns how interfaces are partitioned, not whether a subtype honours a supertype's contract. The two interact (splitting a mutating interface from a read-only one is a common LSP remedy) but answer different questions. Tell: is the defect that clients depend on unused methods (ISP), or that a subtype breaks a guarantee a client depends on when substituted (LSP)?
-
Open–Closed Principle (OCP). The sibling SOLID principle that software entities should be open for extension but closed for modification — typically achieved by extending through abstractions rather than editing existing code. LSP is what makes such extension safe: OCP says extend via subtypes, LSP says the subtypes must be behaviourally substitutable for the extension to preserve correctness. Tell: is the concern being able to add behaviour without modifying existing code (OCP), or that the added subtype does not break clients of the base contract (LSP)?
-
The behavioural-substitutability pattern (umbrella). The substrate-neutral principle LSP instantiates — a specialization may stand in for its generalization only if it honours the contract the generalization fulfilled toward outside clients. This is the super-type, not a peer: it recurs in contract novation, ecological function replacement, and organizational role substitution, each with its own enforcement, whereas LSP is the typed-OO instance with method-signature variance and compiler-invisible-bug framing. Tell: strip away the type hierarchy and the four variance conditions and what remains — "a stand-in must preserve the promises of what it replaces" — is the umbrella (treated more fully in Structural Core vs. Domain Accent); LSP is the software-keyed special case.
Neighborhood in Abstraction Space¶
Liskov Substitution Principle sits in a moderately populated region (44th percentile for distinctiveness): it has near-neighbors but no dense thicket of look-alikes.
Family — Unclustered & Miscellaneous (309 abstractions)
Nearest neighbors
- Type Inference — 0.86
- Refused Bequest — 0.86
- Type System — 0.85
- Primitive Obsession — 0.84
- Relational Model — 0.84
Computed from structural-signature embeddings · 2026-07-12