← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Parallel Inheritance Hierarchies

Code Quality Intermediate
debt(d7/e7/b7/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints list phpmd and phpstan but explicitly marks automated as 'no', and the code_pattern description ('every new Animal subclass requiring a new AnimalRenderer subclass; class hierarchies growing in lockstep') requires a human reviewer to spot the cross-hierarchy coupling pattern. The smell is especially hard to catch when hierarchies live in different namespaces or packages, as noted in common_mistakes.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says hierarchies should be merged or one should reference the other — either path (merging two inheritance trees or introducing delegation throughout) touches multiple files and classes across the codebase. This is not a single-line patch nor a single-component change; it restructures the object model wherever both hierarchies are used.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). The smell applies across web, cli, and queue-worker contexts. Every new feature that touches either hierarchy must touch both, and the coupling is invisible (misconception: 'keeping them in sync is exactly the problem'). The parallel structure shapes how every new subclass is added, imposing a persistent, cross-cutting design tax on all maintainers.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field is explicit: developers believe parallel hierarchies are acceptable as long as they stay in sync, not realising that keeping them in sync IS the problem. Common_mistakes reinforce this — teams add classes to both hierarchies simultaneously 'because that's how it's always worked', never questioning the structural coupling until it becomes unmanageable.

About DEBT scoring →

Also Known As

parallel class hierarchies parallel inheritance hierarchy smell

TL;DR

Every time you create a subclass of one class, you must also create a corresponding subclass of another — a sign of entangled designs.

Explanation

Parallel Inheritance Hierarchies occur when two class hierarchies mirror each other — adding a new Animal subclass also requires a new AnimalHandler subclass, and adding a Shape requires adding a ShapeRenderer. The hierarchies are tacitly coupled. The refactoring moves method implementations to one hierarchy using the Strategy or Visitor pattern, collapsing the parallel structure. This smell often appears when rendering, serialisation, or persistence concerns are baked into domain objects rather than separated via interfaces.

Common Misconception

Parallel inheritance hierarchies are acceptable as long as they stay in sync. Keeping them in sync is exactly the problem — every new subclass in one hierarchy requires a new subclass in the other, and the coupling is invisible until someone adds one without the other.

Why It Matters

Parallel inheritance hierarchies mean that adding a subclass in one hierarchy forces adding a corresponding class in another — a sign that the two hierarchies should be merged or one delegated to the other.

Common Mistakes

  • Not noticing the smell because the two hierarchies are in different namespaces or packages.
  • Adding classes to both hierarchies at the same time 'because that's how it's always worked'.
  • Not considering delegation as a way to collapse parallel hierarchies into one.
  • Parallel hierarchies that share naming prefixes — a reliable indicator: UserReport, AdminReport, GuestReport alongside UserFormatter, AdminFormatter, GuestFormatter.

Code Examples

✗ Vulnerable
// Every new shape requires a class in TWO hierarchies
class Shape {}
class Circle extends Shape {}
class Square extends Shape {}

class ShapeDrawer {}
class CircleDrawer extends ShapeDrawer {}
class SquareDrawer extends ShapeDrawer {}
✓ Fixed
// Collapse via Strategy / Visitor — one hierarchy
interface Drawable {
    public function draw(Canvas $canvas): void;
}

class Circle implements Drawable {
    public function draw(Canvas $canvas): void { $canvas->drawCircle($this); }
}
class Square implements Drawable {
    public function draw(Canvas $canvas): void { $canvas->drawSquare($this); }
}
// Adding a new shape = one class, not two

Added 15 Mar 2026
Edited 22 Mar 2026
Views 115
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 1 ping T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 2 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Amazonbot 16 Ahrefs 8 PetalBot 7 Bing 6 ChatGPT 6 SEMrush 6 Google 4 Scrapy 4 Twitter/X 3 Majestic 2 Perplexity 2 Applebot 2 Meta AI 1 Brave Search 1
crawler 65 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: High
⚡ Quick Fix
When adding a subclass in one hierarchy forces you to add a subclass in another hierarchy, the two hierarchies should be merged or one should reference the other
📦 Applies To
any web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
Every new Animal subclass requiring a new AnimalRenderer subclass; class hierarchies growing in lockstep
Auto-detectable: ✗ No phpmd phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: High Context: File Tests: Update


✓ schema.org compliant