Concept · SOLID principles

Liskov substitution principle

Any implementation of an interface must honour its full behavioural contract, so callers can use it without knowing which one they have.

2 minLSPSOLIDbehavioral subtypingsquare rectangle problem

The Liskov substitution principle (LSP), the L in SOLID, comes from Barbara Liskov's 1987 keynote: if S is a subtype of T, then objects of type T may be replaced by objects of type S without breaking the program. Put plainly: an implementation must behave the way callers of the interface are entitled to expect.

Contracts are more than signatures

A compiler checks that method names and types match. It can't check behaviour — and that's where LSP violations live:

  • Silently doing less. An io.Writer that writes fewer bytes than given but returns a nil error breaks the documented contract, and io.Copy will lose data without noticing.
  • Refusing methods. A read-only repository whose Save panics with "not supported".
  • Strengthening preconditions. An implementation that rejects inputs the interface accepts (only positive IDs, only small files).
  • Weakening postconditions. An implementation that guarantees less: unsorted results from a method documented to sort, or eventual consistency where the interface promised read-your-writes.
  • New error types callers can't handle, or panics where the interface returns errors.

The classic illustration is Square extending Rectangle: setting a square's width also changes its height, so code that sets width 5 and height 4 and expects area 20 gets 16.

How to stay on the right side

  • Write down the contract in the interface's doc comment: what inputs are valid, what's guaranteed on success, what errors mean.
  • Run the same test suite against every implementation — a shared contract test that both the Postgres repository and the in-memory fake must pass.
  • If an implementation can't honour the contract, the abstraction is wrong. Split the interface (Reader and ReadWriter) instead of faking support.

LSP is what makes open–closed extension safe: new implementations can be plugged in only if they really substitute for the old ones. It also argues for composition over inheritance, since inheritance makes it easy to inherit promises a subclass can't keep. See the SOLID lesson.

Learn it in depth