There's a stubborn myth in software engineering: that architecture is designed at the start, once, and then just maintained.
The reality is different. Every software system that lasts more than two years has to evolve. The business changes, the market changes, the technology changes, the team grows. An architecture designed for today's needs becomes a constraint within 12 to 18 months.
The alternative to a risky, expensive full rewrite is evolutionary architecture: an approach in which architectural decisions are made incrementally and validated continuously, so the system evolves alongside the business without delivery ever grinding to a halt.
The concept comes from the work of Neal Ford and Rebecca Parsons at Thoughtworks, formalised in the book Building Evolutionary Architectures (O'Reilly). This article is a practical guide for anyone leading a development team at an SME or scale-up.
What is evolutionary architecture?
Evolutionary architecture is an approach in which the software system evolves incrementally, driven by business constraints and continuously validated by automated metrics.
It isn't "not deciding". It's deciding at the right time, with the right information, and checking that each decision holds up over time.
It rests on three principles:
- Guided incremental change: every change to the architecture is small, reversible and measurable. You don't plan big migrations; you evolve the system continuously
- Multiple architectural dimensions: architecture isn't just the code. It includes the data structure, the boundaries between modules, the dependencies between teams, release times. All of them have to evolve together
- Fitness functions as guardrails: automated metrics that validate architectural properties over time. If a change degrades a property that matters, the fitness function catches it before it reaches production
The practical effect: the system stays in production, delivery doesn't stop, and the architecture keeps improving instead of degrading.
Why traditional architecture doesn't hold up
Designing the architecture up front — what's known as "big design up front" — works when requirements are stable and predictable. In modern software, that's rarely the case.
The problem isn't that the initial architecture is wrong. It's that it becomes wrong over time:
- The product evolves in directions nobody foresaw
- The team grows and the dependencies between components become bottlenecks
- The technologies chosen three years ago are now a constraint
- The cost of every change grows because the system is rigid
This is what's known as the cost of change: in a system with a rigid architecture, the cost of every change grows exponentially. Adding a feature that took two days three years ago now takes three weeks. Not because the team got worse, but because the architecture was never allowed to evolve.
Organisations that don't evolve their architecture accumulate constraints. Those constraints are paid for in release speed, product quality and the ability to keep good people. It's a problem that hits the business directly: unreliable roadmaps, missed market opportunities, costs that rise faster than the value they deliver.

What are fitness functions?
Fitness functions are the operational core of evolutionary architecture: automated metrics that validate architectural properties over time and stop the system degrading while the team adds features.
They aren't functional tests. Tests verify that the code does what it's supposed to do. Fitness functions verify that the system keeps the architectural properties that matter.
Three concrete examples:
- Releasability: the time from change to production release stays under 30 minutes. If it crosses that threshold, something in the architecture is becoming a bottleneck
- Modularity: no module has more than three direct dependencies. If a module accumulates more, it's turning into a coupling point that will hold the team back
- Performance: the latency of the main APIs stays under 200 milliseconds at the 95th percentile. If it degrades, either the architecture can't handle the load or unnecessary complexity has crept in
Fitness functions run in the continuous integration pipeline, just like tests, but instead of checking the code's behaviour, they check the health of the system.
A practical example for an SME: a fitness function that measures how many modules need to be touched to add a new payment method. If the answer is "more than two", the boundary between the modules isn't in the right place; knowing that before you start the work changes everything.
You don't need sophisticated tooling to get started. A test that runs in CI and fails when a threshold is crossed is already a fitness function.
How architectural decisions get made
In evolutionary architecture, decisions aren't all made at the start. They're deferred until more information is available, and documented so that anyone can still understand them six months later.
The tool for this is ADRs (Architecture Decision Records): lightweight, plain-text documents that record each architectural choice with three elements.
- Context: what the situation was, what constraints were in play
- Decision: what we chose and why
- Consequences: what changes, what you gain, what you lose
A real example:
ADR-007: Separating the payments module
Context: The payments module is coupled to the orders module. Every change to payments requires testing the entire orders flow (4 hours). The payments team is blocked by the orders team in 3 releases out of 5.
Decision: Extract the payments module behind its own API. Communicates with the orders module asynchronously, via events.
Consequences: The payments team can release independently. The orders → payments flow needs a dedicated integration test. Higher operational complexity (two services instead of one).
Status: Accepted, March 2026.
ADRs aren't bureaucracy: they're the team's memory of why each decision was made. When someone asks in six months' time "why do we use events here instead of a direct call?", the answer is already written down, along with the context that produced it.
The practice is simple: one Markdown file per decision, numbered sequentially, in a folder in the repository. Zero extra tooling.

Incremental evolution vs full rewrite
The temptation is always the same: "let's throw it all away and start from scratch".
It's an understandable temptation. The old system is full of accidental complexity, choices nobody remembers, code that "nobody wants to touch". The idea of a blank slate is appealing.
Full rewrites, though, fail at an alarming rate. The reason is that the old system has years of business logic baked into it: edge cases, exceptions, rules nobody documented but the code still handles. Rewriting means reimplementing all of that from scratch, often without even knowing it exists. Meanwhile the old system keeps evolving, because the business doesn't stop.
The typical result: the rewrite takes twice as long as planned and costs three times as much.
The alternative is incremental evolution. The most effective pattern is the Strangler Fig (named after the plant that grows around a tree until it replaces it):
- Identify the module with the most friction: the one that slows the team down, generates the most bugs, blocks delivery
- Define a clear boundary between that module and the rest of the system (the so-called anti-corruption layer)
- Build the new version of the module behind that boundary
- Migrate traffic gradually from the old implementation to the new one
- Once the migration is complete, remove the old module
The system stays in production for the whole operation. Delivery doesn't stop. The risk stays contained because you're working on one module at a time.
For more on this, see microservices or monolith: when it makes sense to extract a service and when you're better off with a well-structured monolith.
Architecture constrains the organisation (and vice versa)
Conway's law says that the structure of the software reflects the structure of the organisation that produces it. It isn't a theory: it's an empirical observation confirmed by decades of practice.
If teams are organised by technology (a frontend team, a backend team, a database team), the software will end up with three coupled layers that can't evolve independently. If teams are organised by business domain (a payments team, a catalogue team, a logistics team), the software will naturally tend towards independent modules.
Evolving the architecture without touching the organisation doesn't work.
This is the point that most modernisation initiatives underestimate. Organisations spend months redesigning the architecture, but the teams stay organised as before; within a few sprints, the software goes back to reflecting the old structure.
Evolutionary architecture requires the organisation to evolve alongside the software:
- Teams with clear ownership: every module has a team that owns it, with decision-making autonomy over the boundaries of its own domain
- Organisational boundaries aligned with architectural boundaries: if the software is divided into modules, the teams need to be divided the same way
- Communication between teams managed like communication between modules: clear interfaces, explicit contracts, no implicit dependencies
Change management in software isn't a technical project: it's an organisational project led by people with deep technical expertise, because architectural choices constrain organisational choices and vice versa. This is exactly what we do with our Embedded Teams service.
How to tell whether your architecture needs to evolve
You don't need a formal audit to find out whether your architecture is a constraint. Five questions will do:
- Does adding a feature require changes to more than three components? If so, the boundaries between the modules aren't in the right place.
- Is your average release cycle longer than two weeks? If so, the release process is too tightly coupled, or the pipeline is an architectural bottleneck.
- Do teams block one another because of dependencies? If so, the architecture doesn't allow the autonomy you need. (More on dependencies between teams)
- Is there code that "nobody wants to touch"? If so, there's a critical module without clear ownership: the most fragile point in the system.
- Are estimates systematically wrong? If so, the architecture's accidental complexity is making the cost of every change unpredictable.
If the answer is yes to three or more of these, the architecture isn't supporting the business: it's actively holding it back.
To measure the concrete impact, you can use our Consistency Impact Calculator: it estimates how much time and money architectural friction is costing your organisation.
What to do in practice
You don't need grand plans; you need three concrete steps.
In our Targeted Intervention engagements, the architecture that's hardest to evolve is never the technically most complex one: it's the one where the architectural decisions exist only in the heads of 2–3 people. Introducing ADRs (Architecture Decision Records) to a team that has never used them takes less than a week of work – but it takes someone to facilitate the first three decisions, because the format is simple but the habit of documenting architectural choices isn't. The most useful side effect of ADRs isn't the documentation: it's that the team stops arguing about the same decisions every six months.
1. Define 2–3 fitness functions. Choose the architectural properties that matter most for your business: release time, coupling between modules, performance. Write a test that runs in CI and fails when the threshold is crossed. You don't need a framework; a simple script that measures and compares is enough.
2. Record the next five decisions as ADRs. Every time the team makes an architectural decision over the next two weeks, write it down. Context, decision, consequences, status. Five ADRs are enough to build the habit. After a month you won't want to go back.
3. Identify the module with the most friction and isolate its boundaries. Which module causes the most slowdowns, the most bugs, the most blockers between teams? That's the first candidate for incremental evolution. Define a clear boundary (an interface, an API contract) between that module and the rest of the system. You don't need to rewrite it straight away: you need to isolate it. Isolation is the first step towards being able to evolve it.
If you need help working out where to start, identifying the critical module, defining the right fitness functions or designing the boundaries, that's exactly the kind of work we do through Targeted Intervention and Embedded Teams.
Evolutionary architecture isn't a methodology you adopt wholesale. It's a shift in perspective: stop designing perfect systems and start building systems that know how to change. Organisations that do this don't need to modernise, because they never stop evolving.
Further reading: Building Evolutionary Architectures by Neal Ford and Rebecca Parsons (O'Reilly, 2nd ed. 2023), Team Topologies by Matthew Skelton and Manuel Pais (IT Revolution, 2019), and the Thoughtworks Technology Radar. On our site: the hub on architectural evolution, the cost of change in software and why the roadmap is always late.
