What is architecture
To try to stay away from a never-ending discussion, we’re siding with a definition similar to:
The software architecture of a system is the set of structures needed to reason about the system, which comprise software elements, relations among them, and properties of both. (Bas13; p. 4)
For more information on what’s architecture, see [ISO/IEC/IEEE 42010:2011, Systems and software engineering — Architecture description], [Who needs an architect], [Software Architecture in Practice, by Bass, Clements, Kazman], [Enterprise Application Architecture, by Fowler et al].
In today’s world, we cannot keep our architecture constrained to “what is difficult to change”. Especially in the Agile-inspired current projects, requirements (both functional and non-functional) and quality attributes change faster. And should change faster than in Waterfall-inspired current projects.
A solution for those constraints to use evolutionary architectures.
An evolutionary architecture consists of three primary aspects: incremental change, fitness functions, appropriate coupling (For17, ch 1). Contains two critical characteristics: incremental and guided change across multiple dimensions (For17, ch 1). .
Multiple Architectural dimensions
Quality Attributes or “-ilities”
Within systems engineering, quality attributes are realized non-functional requirements used to evaluate the performance of a system. These are sometimes named “ilities” after the suffix many of the words share. They are usually Architecturally Significant Requirements that require architects’ attention “List of system quality attributes”, from https://en.wikipedia.org/wiki/List_of_system_quality_attributes
-ilities are another way of naming the quality attributes
In For13, it is mentioned the list of “-ilities”. They add a new “-ility”: “evolvability”. This quality attribute is already present in genetic systems:
Evolvability is defined as the capacity of a system for adaptive evolution. Evolvability is the ability of a population of organisms to not merely generate genetic diversity, but to generate adaptive genetic diversity, and thereby evolve through natural selection.
“Evolvability”, from https://en.wikipedia.org/wiki/Evolvability
In a literal meaning, if the systems are produced from the mind of the developers1, the evolution would happen from the second (and following) systems they design. This is related to the “Second-System Syndrome” explained in Bro95, essay “The second-system effect” 2. More information [here]
This is not the referenced meaning of “evolvability”. In EA, evolvability is a “[…] meta-characteristic, an architectural wrapper that protects all the other architectural characteristics” (from For13, ch 1). It’s similar to the derived “quality” variable in the [Iron Triangle / Project Management Triangle]. In this case, it is not merely derived, but also guides the choices in other quality attributes.
Incremental change refers to teams building software incrementally, and deploying it. This is a part of Application Lifecycle Management (ALM), central to operations work. The former can be covered by Extreme Programming (XP) practices, and the latter by Continuous Delivery (CD) practices.
In an Agile environment, we postpone difficult choices to the last responsible moment. This allows for maximum knowledge accumulation. We produce small increases in product features, that will gather customer feedback as early as possible. We are optimizing the system for keeping low the cost of change. Using a system that is easy to change, is not only a pleasure, but allows you to move quickly.
In today’s startup environment3, incremental change is more needed than ever, to allow quick and safe change. In medium-sized companies, incremental change allows for delivering the same increment, quicker. In big organizations, incremental change allows for things that were not believed possible.
If our architecture must cope with change, we’re trying to refactor the architecture to adapt to new requirements. It is well defined that refactoring requires tests. The tests for the quality attributes are named fitness functions.
Maybe the most common fitness function is the unit test suite, in itself: it checks adherence to expected correct behaviour.
An architectural fitness function provides an objective integrity assessment of some architectural characteristic(s).
For13, ch 2
An architectural fitness function checks the architectural quality attribute in an objective manner. An example: the unit test suite tests the software matches the business expectations in terms of features. Another example: a test can be set up to check that no circular dependency exists. This test is atomic, automated, static. Another example: The build cycle time is below twice the average build time. This test is holistic, automated, static.
The fitness functions can be characterized along several axes or categories4:
- Atomic vs Holistic: “Atomic fitness functions run against a singular context and exercise one particular aspect of the architecture”. “Holistic fitness functions run against a shared context and exercise a combination of architectural aspects such as security and scalability”. An example: the unit test checks the system in an atomic fashion. The smoke test in production tests the system in a holistic fashion.
- Triggered vs Continual: “Triggered fitness functions run based on a particular event, such as a developer executing a unit test, a deployment pipeline running unit tests, or a QA person performing exploratory testing”. “Continual tests don’t run on a schedule, but instead execute constant verification of architectural aspect(s) such as transaction speed”. An example: unit tests are triggered on a new commit. Site is still up is a continuous test, executed every few minutes / seconds.
- Static vs Dynamic: “Static fitness functions have a fixed result, such as the binary pass/fail of a unit test”. “Dynamic fitness functions rely on a shifting definition based on extra context”. An example: the unit test suite is static. Counting the number of TODOs (to do comments) in the source is dynamic, where you set it to a maximum (current) number, then decrease the count as time goes. You don’t allow for new TODOs to enter the code.
- Automated vs Manual: “fitness functions within an automated context: continuous integration, deployment pipelines, […]”. An example: the unit test suite should be automated. User experience (UX) checking of an interface is sometimes manual.
- Intentional vs Emergent: Intentional are “fitness functions at project inception as they elucidate the characteristics of the architecture”. Emergent are “some fitness functions will emerge during development of the system”.
- Domain-specific: “Some architectures have specific concerns, such as special security or regulatory requirements”. An example: no credit card information is logged on the system logs, to comply with the PCI regulations and audits.
- Temporal: “build a time component into assessing fitness”. An example: “ break upon upgrade test” (a fitness function that is executed when project dependencies are changed). (all of this is explained in For13, ch 2)
These fitness functions also have a lifecycle: they are 1. Detected / Introduced 2. Reviewed 3. Retired / Deprecated
Introducing a new fitness function: the system might already support the desired requirement, therefore it being an anti-regression mechanism, or plain documentation that this requirement is important for us. In case the system does not already support the fitness function, the system is not yet ‘fit’. That indicator can serve as a milestone for the next step, or as a blocker for the next release.
Remember that these fitness functions are not only detected by the product team, but also introduced by them. Many times, the business stakeholders cannot express the requirements properly (either in problem or solution format), therefore is necessary for us to help diagnose/suggest requirements, both from a technical and business point of view.
To review a business function, reflect on whether it is still applicable. It must satisfy the Architectural Dimensions. If/when it stops satisfying these dimensions, reflect on whether the function is still applicable, or should be modified.
To deprecate a fitness function, delete all traces of said function from the documentation, code, processes, etc. To deprecate it, make sure the requirement is no longer there or the requirement has been satisfied by other means (another fitness function(s)). It is not trivial how functions can replace each other (because of the quadrants/axes in which they belong), so documentation is often needed.
In general, these fitness functions tell, in an objective manner, whether our solution satisfies our requirements. They must be maintained as any other process / solution. That implies a cost, that needs to be paid for.
EA advocates using appropriate coupling between components (logical/physical/quanta5).
In the For13 book, they describe different architectural styles, based on their evolvability: from Big Ball of Mud, to Monoliths (see below), Service-Oriented Architecture, and Event-Driven Architecture. Monoliths are considered a single deployment, that can be split in multiple ways:
- Unstructured: essentially, independent classes coordinating. Loosely-related classes. Lack of cohesion
- Layered: each layer represents a technical capability
- Modular: each module represents a business functionality with well-defined isolation between modules. Usually an API (not necessarily REST)
- Microkernel: a plug-in architecture
Your context is very decisive factor in choosing how to couple the modules of your architecture.
Given that ‘software is a learning process’ (Brandolini), as you keep learning on your domain, you may need to modify how the pieces are coupled.
When does it make sense? – Conclusion
Evolutionary architectures make sense when you want to add “evolvability” to your software. Mostly in the Expand phase in the [3X Model].
It makes less sense to add evolvability to your software when you are trying to find the product-market fit. Also, when you are an established business, and requirements are more stable / slower to change. Whatever your business size or maturity, stay away from the Quick & Dirty, to keep your software maintainable.
Evolutionary architectures suggest baking in the “evolvability” into your architecture. Remember the fitness function, the appropriate coupling, and incremental change.
- Bas13: L. Bass, P. Clements, R. Kazman: Software Architecture in Practice, 3rd Edition. Addison-Wesley Professional. Site: https://www.pearson.com/us/higher-education/program/Bass-Software-Architecture-in-Practice-3rd-Edition/PGM317124.html
- Bro95: F. Brooks: The Mythical Man-Month. 2nd Edition. Addison-Wesley. 1995.
- For17: N. Ford, R. Parsons, P. Kua. Building Evolutionary Architectures: Support constant change. O’Reilly. 2017. Site: http://shop.oreilly.com/product/0636920080237.do
- Meaning all people involved in this, including but not limited to software developers, architects, business stakeholders. ↩
- “The second-system effect proposes that, when an architect designs a second system, it is the most dangerous system they will ever design, because they will tend to incorporate all of the additions they originally did not add to the first system due to inherent time constraints. Thus, when embarking on a second system, an engineer should be mindful that they are susceptible to over-engineering it”. Source: https://en.wikipedia.org/wiki/The_Mythical_Man-Month#The_second-system_effect ↩
- In the startup environment, these practices are sometimes not applied, under the illusion of quick & dirty. Many, wrongly, put the focus in the next 2 weeks of sprint. ↩
- Text between “quotes” has been copied from For13, chapter 2 ↩
- Quanta are described as deployment units. Plural. Singular is quantum. ↩