Composition & Inheritance
Why composition avoids rigid hierarchies, when inheritance genuinely earns its place, and polymorphism as a response to variation that is actually there.
A hierarchy picks one axis of variation forever. A field can be swapped. That reversibility — not elegance — is the whole argument.
Real substitutability, a closed and stable set of subtypes, and shared behaviour that is genuinely the same behaviour. Miss any one and you have coupled two types to save typing.
One interface, several implementations, chosen because the behaviour genuinely differs. With one implementation it is not polymorphism — it is a redirect with a type on it.
Depend on the narrowest thing that does the job. That is a different rule from "declare an interface for everything", and it more often means asking for less than for an abstraction.
Four languages, one problem: share behaviour without spending the inheritance slot. Each solution picks a different thing to give up, and knowing which tells you what the code will do under change.