Skip to content

Managing Complexity

First and foremost, for a system to be easy to extend, it must be easy for a developer to understand the scope of the system related to the change. This involves understanding the overall structure of the system at a high level, and the ability to easily drill down and understand the finer-grained detail of the modules, components or classes impacted by the change.

Understanding the overall structure is what makes it possible to identify which components need to change, or are affected by a proposed change.

To enable this, the key driver is managing complexity: as complexity grows, the ability to make changes shrinks.

Successfully managing complexity ensures that:

  • It is easy to add new features
  • It is easy to maintain existing features
  • The effort to add or maintain features stays low throughout the lifetime of the project
  • Changes can be made with minimum risk, because the team understands their scope

Complexity in a software solution can be classified as essential, incidental or accidental.

The split between essential and accidental complexity comes from Fred Brooks’ “No Silver Bullet”, and “incidental” is often used as another word for “accidental”. I find it useful to separate the two: incidental complexity is the cost you accept to run a system, while accidental complexity is the cost you didn’t need to pay.

  • Essential complexity — the inherent difficulty of the real-world problem or business domain itself. You cannot remove it because it reflects the actual rules and needs of the business.

  • Incidental complexity — complexity introduced by choice or as a by-product of supporting the architecture: that is, complexity that does not directly solve the business problem, but is required to deploy, scale, and maintain a modern system.

  • Accidental complexity — unnecessary complexity added by mistake or by poor design decisions.

A bar split into three parts: essential complexity, the problem itself, which has to be accepted; incidental complexity, the cost of running the system, which has to be managed; and accidental complexity, added by mistake, which should be avoided.

Complexity is managed by applying good design principles at each level of the architecture, without overcomplicating the design.

The design should reveal the intent of the system or component (essential complexity): the design should be manageable and understandable and reflect the domain. Robert C. Martin (Uncle Bob) coined the term Screaming Architecture (particularly in the context of use cases, but the idea applies generally). This is also a core tenet of Domain-Driven Design.

A solution architecture should also be fit for purpose, addressing both functional requirements (essential complexity) and non-functional requirements (incidental complexity). Incidental complexity can be managed by analysing the non-functional requirements and the context of the solution and picking appropriate tools and solutions for the job.

Accidental complexity is avoided by applying sound design principles, including the structural concepts covered in Key Concepts (Abstraction, Encapsulation, Modularity, Cohesion, Coupling). Applied to managing complexity specifically:

  • Abstraction — hides low-level implementation details behind high-level interfaces that match the concepts of the problem domain, so a reader doesn’t need to hold the detail in their head to understand the intent.

  • Encapsulation — prevents implementation details leaking out and further coupling modules together.

  • Modularity — splits the system into loosely coupled modules, so each one can be understood, built and tested on its own.

  • Cohesion — keeps a module’s responsibilities closely related, so a change to one concern doesn’t ripple through unrelated code in the same module.

  • Coupling — is kept to a minimum between modules; in particular, avoid incidental coupling: coupling that isn’t required to fulfil the business requirements.

Applying these (and other) design principles reveals the intent of the system, module or component.