Composition builds behavior by holding collaborators; it avoids fragile base-class coupling
Composition means an object holds references to other objects and delegates work to them. Inheritance models an is-a relationship and hardcodes the implementation choice at class definition time; composition models has-a or uses-a and lets you swap behavior at runtime. The classic failure of inheritance is the fragile base class problem: a change in a parent can break subclasses, and deep hierarchies become hard to reason about. Composition keeps interfaces narrow, makes mocking trivial, and keeps the class open for extension without touching the base.
Inheritance is appropriate for genuine is-a relationships with stable contracts, or for abstract interfaces.
Composition is appropriate for behavior reuse, strategy selection, and anything you may want to swap per instance or per test.
Trade-off: composition adds delegation boilerplate. Mitigate with explicit interfaces, getattr forwarding, or plain attributes.
Common mistake: using inheritance only to reuse a method, producing a hierarchy that is conceptually wrong and fragile.
Common mistake: overusing inheritance for cross-cutting concerns like logging or caching; mixins are inheritance too and can make MRO brittle.
Version note: the principle is language-agnostic; Python's cooperative super() and ABCs make both styles practical, so the choice is a design decision, not a language limitation.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience