<arch.design/>
Principles/Liskov Substitution Principle
{ }CodeArchitectureintermediate1987solidliskovsubstitutioninheritance

Liskov Substitution Principle

A subtype must be fully substitutable for its parent — code that works with the base type must work correctly with any derived type, without surprises.

4/5
{ }
Operates at: Code level

Inside a codebase — classes, modules, files

How it works

Barbara Liskov introduced this principle in a 1987 conference keynote. Formally: if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the program.

In practice: a subclass must honour the behavioural contract of its parent. It can't throw exceptions the parent doesn't throw, can't have weaker preconditions than the parent requires, and can't have stronger postconditions than the parent guarantees.

The classic violation is the Square/Rectangle problem. Mathematically, a square IS-A rectangle. But if Rectangle has setWidth and setHeight, and Square overrides both to always set both dimensions equally, then code that creates a Rectangle and changes only the width gets a wrong area calculation — LSP is violated.

LSP applies to interfaces too, not just inheritance. Any implementation of an interface must behave as callers expect from the contract — not just satisfy the method signatures.

Implementation

TypeScript · Go · Rust
// ❌ Square violates the Rectangle contract
class Rectangle {
  constructor(protected width: number, protected height: number) {}
  setWidth(w: number)  { this.width = w; }
  setHeight(h: number) { this.height = h; }
  area() { return this.width * this.height; }
}

class Square extends Rectangle {
  // ❌ Breaking the contract: setWidth changes height too
  setWidth(s: number)  { this.width = this.height = s; }
  setHeight(s: number) { this.width = this.height = s; }
}

function assertArea(r: Rectangle) {
  r.setWidth(4);
  r.setHeight(5);
  console.assert(r.area() === 20); // ❌ fails for Square — returns 25
}

// ✓ Model correctly — no broken hierarchy
interface Shape { area(): number; }

class Rectangle implements Shape {
  constructor(private w: number, private h: number) {}
  area() { return this.w * this.h; }
}

class Square implements Shape {
  constructor(private side: number) {}
  area() { return this.side ** 2; }
}
// Both are substitutable for Shape — neither breaks the other's contract

Why it matters

Violated LSP means you can't trust polymorphism. If some subclasses work but others silently behave differently, you're forced to add type checks — which defeats the purpose of polymorphism and introduces the bugs OCP was meant to prevent.

When to use

  • Whenever you use inheritance or implement an interface
  • When writing a new implementation of an existing interface
  • When overriding methods in a subclass
  • When designing abstract base classes for others to extend

When NOT to use

  • LSP is not optional — it's a correctness property, not a preference
  • If you can't satisfy LSP, prefer composition over inheritance

Trade-offs

+

Polymorphism is trustworthy — any implementation can be substituted safely

Some mathematical IS-A relationships (Square/Rectangle) can't be modelled with correct inheritance

+

Eliminates defensive type-checks in calling code

Designing correct base class contracts upfront is hard — requires anticipating all subtype needs

+

Makes interfaces robust against new implementations

Violations are often subtle and only caught through careful testing

In production

Java Collections

Any List<T> implementation (ArrayList, LinkedList, CopyOnWriteArrayList) is substitutable — collections code never checks the concrete type

Rust trait objects

Trait objects (Box<dyn Trait>) rely on LSP — any type implementing the trait can be used in place of any other

HTTP clients

A mock HTTP client used in tests must satisfy the same contract as the real one — same status codes, same error types

Industry adoption

4/5Widely adopted — mainstream at medium-to-large engineering orgs.

Related principles