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.
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.Writerthat writes fewer bytes than given but returns a nil error breaks the documented contract, andio.Copywill lose data without noticing. - Refusing methods. A read-only repository whose
Savepanics 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 (
ReaderandReadWriter) 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.