1 Taming complexity with hexagons
This chapter covers
- What hexagonal architecture is and why it matters for maintainable software
- How it solves the "accidental complexity" problem in growing codebases
- The core concepts: Ports, adapters, and dependency inversion
- How hexagonal architecture works
If you’ve ever worked on a codebase that started elegant and ended up an unmaintainable tangle, you already know the villain of this story—where adding a simple feature requires touching dozens of files, where tests break mysteriously after seemingly unrelated changes, where every modification feels like defusing a bomb with garden shears. That creeping rot has a name: accidental complexity. It is the extra mess we introduce through our own design choices, our shortcuts under pressure, our well-intentioned abstractions that calcify into brittle concrete.
The most common source of accidental complexity is not complexity itself, but hard-wiring: components become directly coupled to frameworks, databases, user interfaces, external services, and even to each other’s internal assumptions. Once that happens, every change has somewhere else to travel. A database decision leaks into business logic. A transport model becomes a domain model. A shortcut in one part of the system becomes a constraint somewhere else.