Before you start
LSP is the SOLID principle that takes the sharpest read. The formal statement (Liskov + Wing, 1994) is a paragraph of type theory. The everyday version:
A child class must work anywhere the parent works, without surprising the caller.
Three checks
To see if Child really is a special kind of Parent,
ask:
- Promise check. Does
Childpromise everythingParentpromised, and no less? (Overriding a method to raise "not implemented" breaks this.) - No-surprise check. Does existing code that used
Parentkeep working when given aChild? (Square breaks this for Rectangle.) - Rule check. Does
Childkeep every ruleParentpromised? (IfParentsorts a list,Childshouldn't return it in reverse.)
If any answer is "no", you have an LSP problem — usually solved by making the classes siblings under a common parent instead of parent-and-child.
Why Rectangle-Square is famous
It's the tightest possible example against "is-a" thinking:
- In math, every square IS a rectangle.
- In code,
Square(Rectangle)breaks callers.
Why? Because inheritance in code copies BEHAVIOR, not just math-style membership. Rectangle's behavior is "you can change width and height on their own". Square can't keep that behavior AND stay a square. The child-parent relationship fails on behavior.
Lesson: "is-a in English" is not enough. The behavior of the parent has to make sense for every child.
The three-question checklist for interviews
Before writing class B(A):, ask:
- Can B do everything A can, without shrinking anything?
- Can existing users of A safely be handed a B?
- Do B's overrides keep A's promises?
Any "no" → use composition or sibling classes instead.
About the structural tests in this lesson
test_square_does_not_inherit_from_rectangle and
test_square_has_no_set_width are strict checks. They
pass the correct design and fail the tempting-but-broken
one. If you copy the pre-fix code (Square inherits from
Rectangle) they fail immediately — a guardrail against
sliding back into the smell.
Where this shows up in design interviews
Almost every hierarchy in a design problem has an LSP trap:
- Chess — is
KnightMoveaMove? Yes, if all Move promises hold. No, ifKnightMove.can_apply(from, to)narrows what Move accepts. - File system — is
DirectoryaFile? No — files have content; directories have children. Make them siblings underNode. - Parking spots — is
CompactSpotaRegularSpot? Only if every regular spot's promise holds. If not, use siblings.
Interviewers who care about design will ask "why does X inherit from Y?" as a quiet LSP probe. Answer with the three-question checklist and you'll ace it.