---
title: "Prefer composition over inheritance · Agent Wranglers"
url: https://agentwranglers.se/playbook/architecture/solid-and-dry/composition-over-inheritance
updated: 2026-10-20
licence: "All rights reserved"
---

# Prefer composition over inheritance

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [SOLID and DRY](https://agentwranglers.se/playbook/architecture/solid-and-dry)
- Rule: Reuse behaviour by holding an object that does the job, not by extending a class. Inherit only when the child truly is a kind of the parent.
- How: Put shared behaviour in a small object or function and pass it in through the constructor. Keep inheritance at most two levels deep. Mark classes final or sealed unless they are built to be extended. A framework base class you must extend, such as a model or controller, counts as level zero. Every subclass keeps the [parent's contract](https://agentwranglers.se/playbook/architecture/solid-and-dry/liskov-substitution).
- Check: A linter fails on deep inheritance. Examples are Pylint `too-many-ancestors` and PHPMD `DepthOfInheritance`, set to fail above two levels. Pylint `ignored-parents` skips the framework's own classes. PHP-CS-Fixer `final_class` makes classes final by default, as Kotlin already does.

Example:

```ts
// Bad: a report extends a mailer only to reuse send()
class SalesReport extends Mailer { /* ... */ }

// Good: the report is given a mailer and uses it
class SalesReport {
  constructor(private readonly mailer: Mailer) {}
  send(to: EmailAddress) { this.mailer.send(to, this.render()); }
}
```

```php
// Good: final by default, and the mailer is passed in
final class SalesReport
{
    public function __construct(private readonly Mailer $mailer) {}
    public function send(EmailAddress $to): void { $this->mailer->send($to, $this->render()); }
}
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers architecture playbook: https://agentwranglers.se/playbook/architecture/solid-and-dry/composition-over-inheritance`.

- Previous: [Keep the contract in every subtype](https://agentwranglers.se/playbook/architecture/solid-and-dry/liskov-substitution)
- Next: [Keep interfaces small](https://agentwranglers.se/playbook/architecture/solid-and-dry/interface-segregation)
- [SOLID and DRY](https://agentwranglers.se/playbook/architecture/solid-and-dry)
