# 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.

Concept · SOLID principles · https://levelcode.org/concepts/liskov-substitution

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](https://levelcode.org/concepts/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](https://levelcode.org/concepts/composition-over-inheritance), since inheritance makes it easy to inherit promises a subclass can't keep. See the [SOLID lesson](https://levelcode.org/roadmaps/software-engineer/solid).
