// Patrick Louis

Evolutionary Software Architecture

Building Evolutionary Architectures

In a previous post, I’ve underlined the philosophy behind Domain Driven Design, DDD, and now I’d like to move to a practical approach that handles real issues in software development and architecture: requirements that constantly change, and models that are never precise, never current, and/or never using the best technology available. One of the solution to such problems is to build an evolutionary architecture.

To be able to have a discussion we have to understand the part that software architecture plays, which is not straight forward considering the many definitions and re-definitions of it. I’ll note the particularly fascinating ones.

The architecture of a software system (at a given point in time) is its organization or structure of significant components interacting through interfaces, those components being composed of successively smaller components and interfaces. — IEEE definition of software architecture

In most successful software projects, the expert developers working on that project have a shared understanding of the design. This shared understanding is called “architecture.” This understanding includes how the system is divided into components and how the components interact through interfaces. — Ralph Johnson

Going towards more abstract definitions such as the following.

Architecture is the stuff that’s hard to change later. And there should be as little of that stuff as possible. — Martin Fowler

Architecture is about the important stuff. Whatever that is. — Martin Fowler

Stuff that’s hard to change later. — Neal Ford

These definitions barely overlap but there’s still a vague essence joining them which we can extract. We can say that architecture is concerned with the important decisions in a software project, the objects of those decisions, the shared knowledge of them, and how to reason about them. If we view this from an evolutionary architecture standpoint, the best architecture is one where decisions are flexible, easily replaceable, reversible, and deferred as late as possible so that they can be substituted for alternatives that recent experiences have shown to be superior.
Because architecture is about decision-making, it is inherently tied with the concept of technical debt, the compromise of trading time for a design that is not perfect. Keep in mind that debt accumulates and often leads to architectural decay as changes keep coming and entropy increases.

Similarly, due to the vague definition of architecture, the role of architect is hard to describe. Whether it should be a completely separate role, or whether everyone in a team acts as one, is ambiguous. The vociferous software architecture evangelist Martin Fowler prefers the term Architectus Oryzus, referring to architects that are also active contributors on the projects, thus getting direct insights from their involvement.

The software architecture thought process can be applied at two broad levels: the application level and the enterprise level. Application architecture is about describing the structure of the application and how they fit together, usually using design patterns, while enterprise architecture is about the organizational level software issues such as practices, information flow, methodology standards, release mechanisms, personnel related activities, technology stacks enforced, etc.

Design relates to all the well known development design patterns, refactoring techniques, the usage of frameworks, how to bundle components together, and other daily concerns. In an evolutionary architecture, it’s preferable to have an emergent design instead of one that is set up front.

This gives us a good idea of what software architecture is about, so what’s the current state of it, and why do we need a solution such as building evolutionary architectures?

The usual way we develop software today is by fighting incoming changes that we want to incorporate in the current architecture. Software development has a dynamic equilibrium, and currently we find that software is in a constantly unbalanced and unstable state whenever there are changes to be included. That is because even though we’d like to do the right things at the right time, we can’t predict what those decisions should be, predictability is almost impossible. For example, we can’t predict disruptive technologies that don’t exist yet. As the software ages, we juggle changes and new requirements, there’s no room for experimentation, we only respond.
Stakeholders want the software to fulfill architecture significant requirements, also known as the “ilities” and KPI, such as auditability, performance, security, scalability, privacy, legality, productivity, portability, stability, etc. They expect those to not degrade. Hence, we have to find the least-worst trade-off between them and blindly not introduce anything that could hinder them. This is hard to do, be it because of a business-driven change, such as new features, new customers, new market, etc., or be it because of ecosystem change such as advances in technology, library upgrades, frameworks, operating systems, etc.

CBA

In recent years, we’ve seen the rise of agile development methodologies that are meant to replace the waterfall approach. They are more apt at facing this challenge, they create an iterative and dynamic way to control the change process. What we call evolutionary architecture starts from the idea of embracing change and constant feedback but wants to apply it across the whole architecture spectrum, on multiple dimensions. It’s not the strongest that survive, it’s the ones that are the most responsive to change. What is evolutionary software architecture.

Evolutionary architecture is a meta-architecture, a way of thinking about software in evolutionary terms. A guide, a first derivative, dictating design principles that promote change as a first citizen. Here is Neal Ford’s, Rebecca Parson’s, and Patrick Kua’s definition in their book “Building Evolutionary Architectures” which we’ll dissect.

An evolutionary architecture supports guided, incremental change across multiple dimensions.

  • Multiple dimensions

There are no separate systems. The world is a continuum. Where to draw a boundary around a system depends on the purpose of the discussion. — Donella H. Meadows

While the agile methodology is only concerned with people and processes, evolutionary architecture encompasses the whole spectrum including the technical, the data, the domain, the security, the organizational, and the operational aspects. We want different perspectives, and all evolvable, those are our dimensions. The evolutionary mindset should surround it all in a holistic view of software systems. For this, we add a new requirement, an “-ility”, we call the evolvability of a dimension. This will help measure how easily change in a dimension can evolve the architecture — easily be included in the dynamic equilibrium.
For example, the big ball of mud architecture, with its extreme coupling and architectural rotting, has a dimension of evolvability of 0 because any change in any dimension is daunting.
The layered architecture has a one-dimensional structural evolvability because change at one layer ripples only through the lower one. However, the domain dimension evolvability is often 0 when domain concepts are smeared and coupled across layer boundaries, thus a domain change requires major refactoring and ripples through all the layers.
The microservice style of architecture, that hinges on the post-devops and agile revolution, has a structural and domain dimension evolvability of n, n being the number of isolated services running. Each service in a microservice architecture represents a domain bounded context, which can be changed independently of the others because of its boundary. In the world of evolutionary architecture we call such disjunct piece a quantum. An architectural quantum is an independently deployable component with high functional cohesion, which includes all the structural elements required for the system to function properly. In a monolith architecture, the whole monolith is the quantum. However, from a temporal coupling perspective dimension, transaction may resonate through multiple services in a microservice architecture, and thus have an evolvability in the transactional dimension of 0.

  • Incremental change

It is not enough to have a measure of how easy change can be applied, we also need to continually and incrementally do it. This applies both to how teams build software, such as the agile methodology, and how the software is deployed, things such as continuous integration, continuous delivery, and continuous verification/validation.
These rely on good devops practices that let you take back control in complex systems, such as automated deployment pipelines, automated machine provisioning, good monitoring, gradual migration of new services by controlling routes, using database migration tools, using chaos engineering to facilitate the management of services, and more.

  • Guided Change

We can experiment without hassle, trivially and reversibly, with evolvability across multiple dimension and incremental change. But to start the evolutionary process this is what we need: a guide that will push, using experiments as the main stressors, the architecture in the direction we want. We call this selector an evolutionary fitness function, similar to the language used in genetic algorithms for the optimization function.

An Architectural fitness function provides an objective integrity assessment of some architectural characteristic(s).

Fitness functions are metrics that can cover one or multiple dimensions we care about and want to optimize. There’s a wide range of such functions, and this is where the evolutionary architecture shines, it encourages testing, hypothesis, and gathering data in all manner possible to see how these metrics evolve, and the software along with it. Experimentation and hypothesis-driven development are some superpowers that evolutionary architectures deliver.
This isn’t limited to the usual test units and static analysis but extends way beyond simple code quality metrics. These could be automated or not, global or not, continuous or not, dynamic or not, domain specific or not, etc. Let’s mention interesting techniques that can be used for experimentation and that are now facilitated.

  • A/B testing.
  • Canary Releases aka phased rollout.
  • TDD to find emergent design.
  • Security as code, especially in the deployment pipeline
  • Architecture as code, also in the deployment pipeline with test framework such as ArchUnit.
  • Licenses as code, surprisingly this works too.
  • Test in production: through instrumentation and metrics, or direct interaction with users.
  • Feature flags/feature toggles, to toggle behavior on and off.
  • Chaos engineering, for example using the simian army as a continuous fitness function. “The facilitation of experiments to uncover systemic weakness”.
  • Social code analysis to find hotspot in code.
  • Github scientist, to test hypothesis in production while keeping normal behavior.
    • Decides whether to run or not the try block.
    • Randomizes the order in which use and try blocks are run.
    • Measures the durations of all behaviors.
    • Compares the result of try to the result of use.
    • Swallows (but records) any exceptions raised in the try.
    • Publishes all the information.

The benefit of all the experimentation are soon seen, creating a real interactive feedback loop with users, a buffet of options. The dynamic equilibrium takes care of itself and there are fewer surprises.

This is enabled by the team building the evolutionary architecture. Like with DDD, Conway’s law applies, the shape of the organization is directly reflected in the software — You can’t affect the architecture without affecting the people that build it.

So far, we’ve seen that such team should embrace devops and agile development, that’s a given. Additionally, the team should itself be a cocoon for evolution and experimentation. By making it cross-functional, that is every role and expertise should be found in it, and responsible for a single project, we remove the bottlenecks in the organization. We need a team that resembles our architectural quantum.
A small team, one that can be fed by two pizzas — a two-pizzas team — avoids the separation between who decides what needs to be done and who decides how it’s going to be done. Everyone is there and decides together. We talk of teams in charge of products rather than projects.
The size of the team also allows information to flow seamlessly. All can share the architectural and domain knowledge. Methods that can be used are the usual documentation, architectural decision records, pair programming, and even mob programming.

As nice as it is to have teams that are single working units taking the best decisions for their projects, it’s also important to limit their boundaries. Many companies prefer giving loose recommendations about the software stacks teams can use instead of letting them have their own silos of specialized and centralized knowledge. Again, we face the dynamic equilibrium but this time at the team level. The parallel in enterprise architecture is called the “classic alternatives” strategy.
Human governance in these teams shouldn’t be restrictive because it would make it hard to move. However, the teams are guided by their own fitness functions, an automatic architectural governance. The continuous verifications in the delivery pipeline act as the guard-rail mechanism.

There are two big principles that should be kept in mind while applying all the above: “last responsible moment” and “bring the pain forward”. Together, they have the effect of making team members less hesitant and more prone to experiment.

The last responsible moment, an idea from the LEAN methodology, is about postponing decisions that are not immediately required to find the time to gather as much information as possible to let the best possible choice emerge.
This is especially useful when it comes to structural designs and technological decisions, as insights and clear contexts appear late. It helps avoid the potential cost and technical debts of useless abstractions and vendor-locking the code to frameworks. That is in direct opposition to the classical way of doing software architecture where those decisions are taken upfront.

What to remember when taking decisions

Bringing the pain forward, an idea inspired from the extreme programming methodology, is about facing difficult, long, painful tasks instead of postponing them. The more often we encounter them, the more we’ll know their ins-and-outs, and the more we’ll be incited to automate the pain away. It’s the dynamic equilibrium of pain vs time, the pain increases exponentially if we wait.
This is why it’s encouraged to do things like test in production, apply techniques of chaos engineering, rebooting often, garbage collecting services, merging code often, using database migration tools, etc. Eventually, the known-unknowns become known-knowns, the common predictable pain is gone.

In a world where software keeps getting complex, building evolutionary architectures leads into the topic of building robust, resilient, adaptive, and rugged systems. Software is now an intimate part of our lives, and we rely on it, it has real world effects. We could get inspired by the aerospace world and take a look at the checklist manifesto, or we could embrace statelessness and a throwable/disposable architecture (disposable software, erase your darlings), or maybe go the way of the flexible reactive architectures with their self-healing mechanisms. Anything is possible.

In this post, I’ve given my overview of the way I perceive evolutionary software architecture and its place in software architecture as a whole. It is clearly a step forward from the typical static view of architecture and offers a novel and organic approach, as the name implies. None of what is described is necessarily novel but putting all these methods and thinking together is. If you want an in-depth explanation, you can take a look at the O’Reilly book “Building Evolutionary Software” by Neal Ford, Rebecca Parsons, and Patrick Kua. I hope this article kick-starts your journey.






References:




If you want to have a more in depth discussion I'm always available by email or irc. We can discuss and argue about what you like and dislike, about new ideas to consider, opinions, etc..
If you don't feel like "having a discussion" or are intimidated by emails then you can simply say something small in the comment sections below and/or share it with your friends.