If Inheritance is so bad, why does everyone use it?

Object-oriented inheritance divides programmers: many see it as an overused, fragile way to share code and model domains, while others argue it remains valuable in specific areas like GUIs, game engines, and framework design. Commenters contrast inheritance with alternatives such as composition, traits, interfaces, protocols, and ECS, noting that languages like Go, Rust, Kotlin, and modern Java increasingly steer developers away from deep class hierarchies. A recurring theme is that problems often stem less from inheritance itself and more from how it’s taught, the culture around languages like Java, and the tendency to force real-world domains into rigid “is-a” trees instead of choosing the simplest tool for each part of a system.

Scope of Use and Perception

  • Several commenters say they rarely use inheritance directly today, especially in modern C++, Go, Rust, JS/TS, and Python codebases.
  • Others report large, legacy, or Java-heavy systems where inheritance is still pervasive, especially due to historical patterns and teaching.
  • Some see “inheritance is bad” as overreaction; others see it as still overused and misunderstood.

Where Inheritance Works Well (According to Thread)

  • GUI frameworks and native UI toolkits: widget hierarchies, event handling, and “structural control flow” often model cleanly as shallow class trees.
  • Game engines and graphics internals: sometimes use inheritance for engine-side primitives, but gameplay logic tends to migrate to components/ECS.
  • Libraries/frameworks: abstract base classes providing partial implementations, plugin bases, filters, and test doubles are widely regarded as good fits.
  • Ontological / “is‑a” cases: small, stable hierarchies (e.g., expression trees, maps with abstract base classes) can reduce duplication.

Main Critiques of Inheritance

  • Deep or wide hierarchies are hard to reason about, debug, and evolve; changing base classes can break many subclasses (“brittle base class”).
  • It often conflates interface and implementation, making refactors and behavioral changes risky.
  • Real domains rarely form clean trees; multiple orthogonal concerns (e.g., “ageable”, “monster”, “on fire”) fit composition better.
  • In performance-sensitive code (e.g., games), vtables and pointer-chasing can harm cache locality.

Composition, Traits, and Interfaces

  • Many advocate “prefer composition over inheritance” rather than “never use inheritance.”
  • Alternatives mentioned: interfaces/protocols, traits, mixins, delegation, typeclasses, concepts, multimethods, higher‑order functions, ECS/components.
  • Several languages (Go, Rust, Kotlin, Swift, Scala) are cited as steering developers toward these tools and away from implementation inheritance.

Teaching, Culture, and History

  • 1990s–2000s OOP hype, GUI frameworks, and Java curricula normalized deep hierarchies and “cars/animals” examples.
  • Some educators now explicitly warn students that inheritance is often a poor default and emphasize functions, data shapes, and composition first.
  • A recurring claim: many pathologies blamed on OOP/inheritance are sociological (big-org Java culture, over-architecting, design-pattern fashion) as much as technical.

Related Analogies

  • CSS cascade/inheritance is compared to class inheritance: powerful but hard to scale; utility-class and BEM styles are framed as “composition over inheritance” in CSS.