← experimental
Contents
  1. Key Claims
  2. Definition
  3. Evolvability as a Meta-Characteristic
  4. The Three Pillars
  5. Evolvability by Architecture Style
  6. Refactoring vs Restructuring
  7. Adaptation vs Evolution
  8. Guidelines for Building Evolvable Architectures
  9. Fitness Function Categories for API Systems
  10. Hypothesis-Driven Development
  11. API Layer Cake (Antipattern)
  12. Major Antipatterns (evo-arch Ch 7)
  13. APIs as Seams (api-arch perspective)
  14. Strangler Fig Pattern
  15. The Six Rs of Cloud Migration
  16. Type 1 vs Type 2 Decisions
  17. When to Build Evolutionary Architecture (and When Not to)
  18. Form Follows Failure (Nygard)
  19. Bad Layering as an Evolutionary Obstacle (Nygard)
  20. Decision Loop Speed and Thrashing
  21. How Different Sources Treat It
  22. Related Concepts
  23. Key Takeaways

Key Claims

  • Three pillars. Incremental change (Deployment Pipelines make small changes safe), fitness functions (objective automated governance), and appropriate coupling (matching coupling tightness to business necessity).
  • Inappropriate coupling is the root cause of non-evolvability. Most architectures fail to evolve because their coupling didn't match the change vector. The decoupling lever varies by axis: deployment, runtime, data, schema, organisational.
  • Evolvability varies by architecture style. Microservices (highest), event-driven (high), service-based (medium-high), down to Big Ball of Mud (none). The limiting factors are quantum size and the presence of cross-quantum transactions.
  • Migration follows established patterns. Strangler Fig for external seams, Branch By Abstraction for embedded capabilities, Parallel Run for correctness verification. Big-bang rewrites have a high failure rate; the safe path is incremental.
  • Architecture decisions are reversible at different costs. Type 1 decisions (irreversible) warrant ADRs and extensive deliberation; Type 2 (reversible) deserve experimentation. Treating Type 2 decisions as Type 1 is the dominant antipattern.
  • Testability + deployability are the operational drivers. Farley's framing: an evolvable architecture is one whose individual components can be tested in isolation and deployed independently. TDD acts as real-time architectural feedback — code that's hard to test is poorly modularised.
  • Form follows failure. Architectural patterns emerge in response to specific past failures (Sidecar emerged because cross-cutting concerns kept breaking; Circuit Breaker because cascading failures kept recurring). Knowing the failure makes the pattern make sense.

Definition

"An evolutionary architecture supports guided, incremental change across multiple dimensions." — Ford, Parsons, Kua (→ Building Evolutionary Architectures Ch 1)

Three words carry the weight:

  • Guided — change is directed by fitness functions and architectural principles, not left to drift
  • Incremental — changes are small and safe, not big-bang rewrites; supported by deployment pipelines and automated tests
  • Multiple dimensions — not just code structure but also data, security, performance, operational, and compliance concerns evolve simultaneously

Evolutionary architecture is not the same as "agile architecture" or "emergent design." It requires deliberate upfront identification of the dimensions that matter and explicit fitness functions to govern those dimensions continuously.

Evolvability as a Meta-Characteristic

Evolvability is not simply one more "-ility" alongside scalability and availability — it is a meta-characteristic: an architectural wrapper that protects all the other architectural characteristics as the system changes over time (→ Building Evolutionary Architectures Ch 1).

The architectural bit-rot problem: architects design a system with deliberate characteristics (scalability, performance, security), then expose it to real-world development. Developers gradually erode those characteristics — bypassing layering for performance, adding tight coupling for convenience. Without active protection, the architecture degrades silently. Fitness functions are the protection mechanism.

"Adding evolvability as an architectural characteristic implies protecting the other characteristics as the system evolves. Thus, evolvability is a meta-characteristic, an architectural wrapper that protects all the other architectural characteristics." (Ch 1)

The term "evolutionary" was chosen deliberately over alternatives:

  • Adaptable/agile: captures change over time but says nothing about the direction of change
  • Emergent: implies change arises without intent — the opposite of guided
  • Incremental: captures only one of the three pillars, not the governance aspect
  • Evolutionary: implies both the process (small guided changes) and the goal (fitness-tested improvement)

The Three Pillars

Building Evolutionary Architectures organises the discipline around three mutually reinforcing pillars (→ Building Evolutionary Architectures Ch 1):

1. Incremental change — the operational capability to make small, safe changes frequently. Enabled by Deployment Pipelines, automated fitness functions, and high test coverage. Measured by cycle time: v ∝ c (evolution speed is proportional to cycle time).

2. Fitness functions — the governance mechanism. Any mechanism that provides an objective, repeatable assessment of an architectural characteristic (→ Fitness Functions). Includes automated tests, architectural checks, chaos engineering, monitoring alerts, and manual reviews. Fitness functions are identified upfront for each dimension and automated in the deployment pipeline.

3. Appropriate coupling — matching coupling tightness to business necessity. Inappropriate coupling (shared databases without schema ownership, shared libraries coupling microservices, vendor lock-in) is the primary cause of architectural non-evolvability. The architecture quantum (→ Architecture Quantum) defines the scope of coupling that must be managed.

Evolvability by Architecture Style

Evo-arch rates each major architecture style for evolvability based on coupling, quantum size, and independent deployability (→ Building Evolutionary Architectures Ch 4):

Style Evolvability Primary Limiting Factor
Big Ball of Mud None No coupling control; no fitness function surface
ESB-driven SOA Poor Central orchestration hub; vendor coupling
Mediator EDA Low Mediator reintroduces coupling
Layered monolith Low–Medium Technical partitioning; full deployment required for any change
Microkernel Medium Plug-in contracts; single quantum
Service-based Medium–High Shared database; coarse quanta
Broker EDA High Decoupled event channels; fine-grained quanta
Microservices Highest Maximum decoupling; smallest practical quanta

Transactions as a constraint: two-phase commit across service boundaries effectively merges those services into a single quantum (the "strong nuclear force" of architecture). This is why service-based architectures — which support ACID transactions within coarse services — score lower on evolvability than microservices, which require saga/eventual consistency patterns instead.

Refactoring vs Restructuring

A distinction the book introduces in Ch 6 that is often blurred: refactoring (Fowler's definition) changes internal structure without changing external behaviour — the same inputs produce the same outputs, and architectural characteristics are preserved. Restructuring changes the architectural characteristics themselves: you are not preserving the old behaviour, you are changing what the system optimises for.

Fitness functions are the protection mechanism during restructuring: they continuously verify that the characteristics being preserved (performance, security, data integrity) remain intact as the internal structure changes. The GitHub Scientist pattern (Ch 3) is the canonical restructuring tool — it runs old and new code paths simultaneously, compares outputs, and guards against regressions without stopping production traffic.

"Refactoring means preserving external behaviour. Many architectural changes don't actually preserve external behaviour—they change priorities." (Ch 6)

Adaptation vs Evolution

A further distinction from Ch 8: adaptation layers new behaviour alongside old — the original implementation is preserved and new capabilities are bolted on. Adaptation is sometimes necessary (feature flags, compatibility shims) but accumulates technical debt proportional to the number of adaptation cycles. Each adaptation cycle increases the number of parallel code paths.

Evolution changes the architecture in situ, protected by fitness functions. The end result is a system that continues to change without accumulating a legacy of outdated solutions. The goal of evolutionary architecture is to support evolution, not merely adaptation.

Feature flags are an example of intentional, bounded adaptation: used temporarily during A/B experiments or incremental rollouts, then removed once the decision is resolved. The Knight Capital incident demonstrates what happens when temporary adaptations become permanent: the retained feature flag, reactivated on one un-updated server, cost $440M in 45 minutes.

Guidelines for Building Evolvable Architectures

Practical guidelines from evo-arch Ch 6:

Guideline Implication
Remove needless variability Immutable infrastructure; no snowflake servers
Make decisions reversible Blue-green deployments; feature flags (clean up after each migration)
Prefer evolvable over predictable Design for unknown unknowns; avoid premature optimisation for predicted requirements
Build anticorruption layers JIT Abstract external dependencies; delay commitment to the last responsible moment
Build sacrificial architectures Intentional throw-away systems for MVP; replace when validated
Mitigate external change Prefer pull updates (libraries) to push updates (frameworks); wrap transitive dependencies
Libraries vs frameworks Libraries are passive (update when needed); frameworks are active (update aggressively — they drive your architecture)
Prefer Continuous Delivery to snapshots Snapshots create speculative updates and stale dependencies
Version services internally Hide multiple API versions within the service; support no more than two simultaneously

Fitness Function Categories for API Systems

Chapter 8 of Mastering API Architecture elaborates the fitness function categories specifically for API-based systems (→ Mastering Api Architecture Ch 8):

Category What to Measure
Code Quality Test coverage, cyclomatic complexity, static analysis results
Resiliency Error rate in pre-production under synthetic traffic; fault injection via gateway or mesh
Observability All services publish required metric types (RED); structured log format compliance
Performance Latency and throughput targets; automated in the build pipeline; requires production-like data
Compliance Audit requirements, data governance rules, GDPR/PCI evidence
Security Dependency vulnerability scans; automated OWASP-style static analysis
Operability Monitoring and alerting configured; runbooks exist; on-call rotation in place

ADRs should document the initial fitness function decisions — what to measure, what threshold constitutes failure, and what pipeline stage the check runs in.

Hypothesis-Driven Development

A technique enabled by evolutionary architecture that replaces requirements with experiments (→ Building Evolutionary Architectures Ch 3). Rather than gathering formal requirements and building features based on analyst judgement, teams frame new capabilities as hypotheses:

"If we make the sales images bigger, we hypothesize that it will lead to a 5% increase in sales for those items."

The hypothesis specifies: what is being tested, what experiment will be run, and what confirming the hypothesis means for future development. Experiments run via A/B testing — a portion of users see the proposed change; the rest see the existing behaviour. Results from real user behaviour confirm or refute the hypothesis before committing to the full feature.

Why it matters architecturally: hypothesis-driven development requires service-based or microservices architecture to support simultaneous multiple versions of a feature, modern DevOps practices for fast deployment of experiment variants, and feature flags to route users to different variants. It is only possible when cycle time is short enough to run meaningful experiments.

The mobile.de case: a product accumulated features over years that degraded the overall user experience, but no analyst knew which features were valuable. Rather than debating which of three UI approaches to take, the team built all three variants and let users decide through A/B testing. Evolutionary architecture enables this by making variant creation and routing inexpensive.

"Experiments should run long enough to yield significant results. Generally, it is preferable to find a measurable way to determine better outcomes rather than annoy users with things like pop-up surveys." (Ch 3)

API Layer Cake (Antipattern)

The "layered APIs" or "API layer cake" pattern applies the classic enterprise tier model to APIs: Systems of Engagement (SoE) → Systems of Differentiation (SoD) → Systems of Record (SoR), based on Gartner's Pace-Layered Application Strategy (→ Mastering Api Architecture Ch 8).

This pattern has a poor track record for the same reasons as layered monoliths: it encourages shortcuts (presentation tier directly calls the datastore tier), leads to functionality duplication between layers, and creates a situation where any change to a business capability requires modifying multiple layers in lockstep. Generally avoid this pattern in favour of domain-oriented, highly cohesive APIs aligned to bounded contexts.

Major Antipatterns (evo-arch Ch 7)

Antipattern Summary
Vendor King Architecture coupled to a vendor's product roadmap; vendor constraints become architectural constraints
Code Reuse Abuse Shared libraries coupling microservices; prefer duplication to coupling in distributed architectures
Reporting OLTP and OLAP in the same service; analytical queries create inadvertent coupling to operational data models
Inappropriate Governance One-size-fits-all governance; solution is Goldilocks Governance (three technology stacks: simple/medium/complex)
Planning Horizons Sunk cost fallacy applied to architecture; irrational attachment to past decisions prevents needed restructuring

See Evolutionary Database Design for the Reporting antipattern in depth.

APIs as Seams (api-arch perspective)

Michael Feathers' concept of a seam — a place where loosely coupled substitution is possible — applies directly to API boundaries (→ Mastering Api Architecture Ch 8). An API is a seam because:

  • The consumer depends on the contract (OAS, .proto), not the implementation
  • The producer can be replaced, replatformed, or rewritten without the consumer knowing, as long as the contract is honoured
  • The API gateway provides location transparency: the consumer calls a stable route regardless of where the backend is deployed

Seams are why API-first design matters architecturally: a well-designed contract makes the implementation replaceable; a poorly designed one couples the consumer to implementation details and eliminates the seam.

Strangler Fig Pattern

The canonical pattern for migrating a legacy system to a new architecture without a full rewrite (→ Mastering Api Architecture Ch 8):

  1. Facade: deploy an API gateway or facade in front of the legacy system
  2. Parallel services: implement new capabilities as separate services behind the facade
  3. Route migration: progressively route traffic from the legacy to new services (canary → full cutover)
  4. Retire: decommission the legacy when it handles no traffic

Facade vs adapter: the gateway should act as a facade (routing transparently), not an adapter (transforming data models or business logic). Adapter logic in the gateway creates coupling between gateway configuration and domain schema.

The Six Rs of Cloud Migration

Framework for classifying migration decisions per component (→ Mastering Api Architecture Ch 9):

Strategy Description When to Use
Retain Keep on-premises Regulatory constraint, dependency blocker
Rehost Lift and shift Large estate, short timeline; minimal benefit
Replatform Move with targeted optimisations Some cloud benefit without full refactor
Repurchase Replace with SaaS Commodity functions (CRM, HRIS)
Refactor / Re-architect Redesign cloud-native Competitive differentiation, scalability
Retire Decommission Unused, superseded, or consolidated

The strangler fig is the execution mechanism for Replatform and Refactor strategies.

Type 1 vs Type 2 Decisions

Not all architectural decisions carry the same reversibility cost (→ Mastering Api Architecture Ch 8):

  • Type 1 (irreversible): API gateway selection, service mesh adoption, exchange format (REST/gRPC), authentication framework. These create path dependencies across many consumers. Must be deliberate and documented in Adrs.
  • Type 2 (reversible): deployment strategies, feature flag tooling, observability vendors. Can be changed without breaking consumer contracts; lighter process is appropriate.

When to Build Evolutionary Architecture (and When Not to)

From Ch 8: evolutionary architecture is not appropriate in all contexts.

Build it when:

  • Cycle time is a competitive differentiator in your market (A/B testing, hypothesis-driven development, fast response to market shifts require short cycle times)
  • Scale matters (coupling at any architectural point eventually limits scale; decoupling enables both evolvability and scale — the Amazon monolith-to-microservices story)
  • Advanced DevOps capabilities are desired (A/B testing is structurally impossible in highly coupled systems)
  • The business faces the Innovator's Dilemma: disruption can come from smaller competitors with faster, more evolvable systems

Do not build it when:

  • The architecture is an irredeemable Big Ball of Mud: the effort to make it evolvable exceeds the cost of rewriting from scratch. First, find the modularity that does exist; if the untangling cost is too high, rewrite is the answer.
  • Other architectural characteristics dominate: LMAX achieved 6 million transactions/second on a single Java thread by fitting logic into CPU cache and pre-allocating memory to prevent GC. That extreme optimisation for throughput makes evolvability secondary — the architecture is domain-specific, not general.
  • The system is deliberately sacrificial: MVP architectures exploring market viability should be designed to throw away. Martin Fowler coined the term; Twitter's first architecture is the canonical example. Replace when validated; do not invest in evolving something intended to be replaced.
  • Short business horizon: if the company plans to close or pivot completely within a year, evolvability investment is waste.

Form Follows Failure (Nygard)

Nygard (→ Release It ch. 16) draws on Henry Petroski's The Evolution of Useful Things to reframe how architectural evolution actually happens: "Form follows failure, not function." Changes to design — of physical artifacts and software systems alike — are motivated more by what the prior version does poorly than by what it does well. Systems evolve through their failures; the initial design is rarely the driver of long-term shape.

The practical implication: the most honest way to understand why a system looks the way it does is to study the failures and constraints it was designed to work around, not the stated requirements.

Bad Layering as an Evolutionary Obstacle (Nygard)

Standard layered architectures enforce vertical isolation (UI / domain / persistence) but tolerate — and encourage — horizontal coupling: "god" domain classes that everything touches, where any change requires a drilling expedition through all layers and produces commits touching Foo, FooController, FooFragment, FooMapper, FooDTO.

The problem: one layer's decomposition dominates the others. When a new concept enters the domain, it casts shadows through every layer. This is not a layer isolation problem — it is a coupling problem in disguise.

Component-based decomposition (rotating the barriers 90 degrees): each component owns its full stack from storage through API/UI; components communicate only through narrow formal interfaces; components are substitutable independently. If components run in separate processes, this is microservices. If in the same process, it resembles "self-contained systems."

Each component boundary creates an option (→ Baldwin & Clark's modular operators) to split, substitute, augment, or exclude the component independently.

Decision Loop Speed and Thrashing

Nygard (→ Release It ch. 16) frames organisational adaptability as the rate of the decision loop: sense → decide → act → observe. Getting inside the competitor's decision loop (acting faster than they can react) is the decisive competitive advantage.

Thrashing is the failure mode of going too fast: action rate exceeds feedback rate, so each new direction is initiated before the prior one has been evaluated. Aviation calls this "pilot-induced oscillation" (porpoising). Fix: do not slow down delivery; instead, speed up the feedback side — build an experimentation platform to accelerate observation and decision, rather than deploying more build tooling.

How Different Sources Treat It

Source Perspective
Building Evolutionary Architectures Originating source; formal definition, three pillars, evolvability ratings per style, guidelines, antipatterns — evolutionary architecture as the central subject
Release It "Form follows failure" (Petroski); bad layering as horizontal coupling obstacle; component-based decomposition; modular operators (Baldwin & Clark); decision loop speed and thrashing; service extinction as the critical evolutionary mechanism
Mastering Api Architecture API-specific angle: APIs as seams, strangler fig at gateway level, six Rs for cloud migration, Type 1/2 decisions; fitness functions for API governance
Fundamentals Of Software Architecture Applies evolutionary architecture thinking to monolith-to-microservices migration; fitness functions as one component of the governance layer
Learning Domain Driven Design DDD-centric brownfield modernisation: (1) Strategic analysis (understand business domain → identify subdomains → chart context map of current design); (2) Strangler pattern used with an ACL/OHS layer — facade routes traffic; legacy and modernised context may temporarily share a database; (3) Refactoring tactical design incrementally (start with value objects, then aggregates, don't jump to event sourcing); (4) "Pragmatic DDD": DDD is about business-domain-driven design decisions, not mandatory use of all patterns; "undercover DDD" — use tools individually without requiring org-wide adoption (ch. 13)
Monolith To Microservices Migration-focused treatment: strangler fig as the primary incremental extraction pattern (HTTP proxy, FTP, and message variants; deployment ≠ release; feature freeze during migration); branch by abstraction for deeply embedded capabilities; parallel run and verify variant for correctness verification; progressive delivery as the umbrella strategy. Positions evolutionary architecture practices as prerequisites for safe microservice migration (ch. 3).
Software Architecture Metrics Farley (ch. 3): testability and deployability as the two operational drivers of evolutionary architecture; designing for testability produces the five sustainable design attributes (modularity, cohesion, SoC, abstraction, coupling); TDD as real-time architectural feedback; deployment pipeline scope = independently deployable unit; architectural descriptions as "tourist maps" — highlight landmarks, suppress irrelevant detail; do not over-engineer for hypothetical requirements, keep options open through testability and short deploy cycles.
  • Fitness Functions — the governance mechanism that makes evolution safe
  • Deployment Pipelines — the automation mechanism for incremental change
  • Architecture Quantum — quantum size as the primary evolvability lever
  • Conways Law — team structure as an architectural decision; Inverse Conway Maneuver
  • Evolutionary Database Design — data dimension of evolutionary architecture
  • Adrs — documents irreversible decisions that define the evolution path
  • Api Design — contract stability that makes incremental migration possible
  • Api Gateway — provides location transparency enabling the strangler fig
  • Modular Monolith — intermediate migration step: domain-partitioned single deployment with fitness-function-enforced coupling, before extracting services
  • Strangler Fig — the primary pattern for incremental migration of an existing system
  • Branch By Abstraction — for migrating deeply embedded capabilities with no external seam
  • Parallel Run — correctness verification during coexistent dual-implementation phases
  • Progressive Delivery — staged rollout patterns (canary, blue-green, ring, dark launch, feature flag) that make evolutionary change safe
  • Feature Flags — application-layer release control; deployment ≠ release at the code level
  • Cost As Architectural Force — change cost as a compounding architectural property

Key Takeaways

  • Build for evolution, not for the end state. The three pillars (incremental change, fitness functions, appropriate coupling) make change cheap; designing for an imagined future is usually wasted work.
  • Coupling decisions are evolvability decisions. Inappropriate coupling — coupling tighter than the change axis requires — is the dominant cause of non-evolvability. Loosen coupling along the axes that actually change.
  • Migration is incremental. Strangler fig for external seams, branch by abstraction for embedded capabilities, parallel run for correctness checks. Big bang rewrites have a high failure rate that doesn't drop with team experience.
  • Type 1 decisions deserve ADRs; Type 2 deserve experiments. Treating Type 2 (reversible) decisions as Type 1 (irreversible) is the dominant antipattern; teams over-deliberate on choices they could just try.
  • Testability and deployability are the operational drivers. If individual components can't be tested in isolation and deployed independently, the architecture is not evolvable regardless of style.