← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Liskov Substitution Principle

Code Quality Intermediate
debt(d5/e5/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). detection_hints lists phpstan and psalm — these are specialist static analysis tools that can catch some LSP violations (e.g. narrowed parameter types, return type mismatches), but subtle behavioural violations like empty overrides or silent postcondition weakening still require careful code review. Not caught by the compiler or default linters.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says to use composition instead if a subclass can't honour the contract. Restructuring an inheritance hierarchy to use composition typically touches the subclass, its callers, and the parent — spanning multiple files — and is not a simple one-line or single-pattern replacement.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). LSP applies across web, cli, and queue-worker contexts. A violated substitution principle in a shared base class can silently corrupt behaviour in many consumers, and the debt compounds as more subclasses or callers are added over time. It's not architectural (b7+) but does impose a meaningful, ongoing tax across multiple workstreams.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception field is explicit: developers commonly believe LSP only requires subclasses to keep method signatures intact. In reality, LSP is about honouring the full behavioural contract — preconditions, postconditions, and invariants. This directly contradicts the natural reading of 'substitution' and how most OOP tutorials introduce inheritance, making it a serious cognitive trap that contradicts common mental models.

About DEBT scoring →

Also Known As

LSP Liskov Substitution Principle behavioural subtyping

TL;DR

Subtypes must be substitutable for their base types without altering the correctness of the program.

Explanation

The L in SOLID, LSP requires that a subclass can replace its parent class in any context without breaking the application. Violations include subclasses that throw exceptions for methods the parent allows, weaken postconditions, or strengthen preconditions. A classic example is a Square extending Rectangle: setting width on a Square also changes height, violating Rectangle's contract. PHP's type system and interface contracts enforce syntactic compatibility, but LSP is a semantic guarantee that requires design discipline.

Common Misconception

LSP just means subclasses must not remove parent methods. LSP requires that subclasses honour the behavioural contract of the parent — a subclass that throws where the parent returns, or weakens postconditions, violates LSP even if it keeps all method signatures.

Why It Matters

LSP ensures that subclasses can replace their parent without breaking the program — violations cause unexpected behaviour when code works through the parent type but receives a subclass at runtime.

Common Mistakes

  • Throwing a NotImplementedException in a subclass method — a subtype that cannot fulfil the contract is not a valid subtype.
  • Strengthening preconditions in subclasses: parent accepts any string, child only accepts non-empty — callers that pass '' break.
  • Weakening postconditions: parent guarantees non-null return, child may return null — breaks null-safe callers.
  • Overriding a method to do nothing (empty body) — silent contract violation.

Code Examples

💡 Note
Subtypes must not strengthen preconditions or weaken postconditions. Square changing both dimensions when only one is set violates caller expectations.
✗ Vulnerable
class Rectangle {
    public function setWidth(int $w): void  { $this->w = $w; }
    public function setHeight(int $h): void { $this->h = $h; }
}

class Square extends Rectangle {
    // Violates LSP: setting width forces height too
    public function setWidth(int $w): void  { $this->w = $this->h = $w; }
    public function setHeight(int $h): void { $this->w = $this->h = $h; }
}
✓ Fixed
interface Shape {
    public function area(): float;
}

class Rectangle implements Shape {
    public function __construct(private float $w, private float $h) {}
    public function area(): float { return $this->w * $this->h; }
}

class Square implements Shape {
    public function __construct(private float $side) {}
    public function area(): float { return $this->side ** 2; }
}

Added 15 Mar 2026
Edited 22 Mar 2026
Views 132
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 2 pings S 1 ping M 1 ping T 0 pings W 1 ping T 0 pings F 2 pings S 2 pings S 0 pings M
No pings yet today
Bing 1 Amazonbot 1
Ahrefs 15 Scrapy 15 Amazonbot 10 Perplexity 9 SEMrush 9 Google 8 PetalBot 8 Unknown AI 5 Bing 5 Majestic 2 ChatGPT 2 Applebot 2 Meta AI 1 Twitter/X 1 Sogou 1
crawler 88 crawler_json 3 your_contextpost 1 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
Verify subclasses don't throw new exceptions the parent doesn't, don't require stricter preconditions, and don't weaken postconditions — if they do, use composition instead
📦 Applies To
any web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Subclass throwing NotImplementedException; child class narrowing parameter type; override that changes observable behaviour unexpectedly
Auto-detectable: ✓ Yes phpstan psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: High ✗ Manual fix Fix: High Context: Class Tests: Update


✓ schema.org compliant