202605281610 Building Evolutionary Architectures
An evolutionary software architecture supports guided, incremental change across multiple dimensions.
Part I — Mechanics
The mechanics of an evolutionary architecture concern the engineering practices and verification that allow an architecture to evolve.1 This is the nuts and bolts of actually evolving the system.
Chapter 1 — Evolving Software Architecture
- Evolving architecture is hard. Were trying to hit the constantly moving target (what our business requires as it changes and grows) while standing on shifting sands (the state of the art in software development and technology).
- 202205031449 Optimize for change.
- Make incremental changes via architecture and deployment.
- Protect your architectural dimensions with fitness functions.
Chapter 2 — Fitness Functions
- 202606021226 Fitness functions
- 202109251100 Verify behaviors and assert properties
- Objective architectural fitness functions form the primary mechanisms for implementing evolutionary architecture.
- Monitors, metrics, chassis engineering, architecture testing frameworks, tests, and security scanning are just some of the tools at our disposal for measuring and upholding fitness functions.
- Fitness functions can be atomic, holistic, or somewhere in between. Each has value and tradeoffs. Similarly they can be static vs dynamic, automated vs manual, intentional vs emergent, domain specific or generic.
- There’s value in rallying around the unifying concept of fitness functions. It brings different a sometimes conflicting goals into one framework that you can centralize and work on.
Chapter 3 — Engineering Incremental Change
- Versioning, CI, CD, etc. are all required competencies for an engineering organization to be able to deliver incremental changes.
- Use feature flagging and test things that you can in production to ensure full correctness before rolling to out to everyone.
- Q: how to balance speed to deploy and rollback with correctness? No one right answer obviously, but the example pipelines here are massive.
- Contract testing can be a powerful tool to subvert the need to do full integration environments. See Pact.
- The key to (and sanity in) software engineering discipline lies in incremental change with automated verification. True now more than ever.
Chapter 4 — Automating Architectural Governance
- Marrying incremental changes and CI/CD with fitness functions was an evolution in software engineering in the 90's and 2000's that allowed automating architectural governance from the lowest to the highest levels. (202406271256 Essential XP Emergent Design)
- Afferent coupling measures the number of incoming connections to a code artifact (module, component, class, etc.) while efferent coupling measures the number of outgoing connections. 202204272309 Modules should be loosely coupled.
- The abstractness metric ()is the ratio of the number of abstract code artifacts () (interfaces, abstract classes, etc.) to the sum of all the abstract and concrete artifacts () together.
- The instability metric where Instability () is the ratio of efferent coupling () to the total of efferent and afferent coupling () and represents the amount of instability in a codebase where instability means the amount of change that is caused by coupling (e.g. change that was needed but really shouldn't have been).
- The normalized distance from the main sequence metric for a code base combines the abstractness and instability metrics into one formula that measures if there is a similar mix of abstractness and instability. It is supposed to represent a healthy tension between these two competing concerns.
- More testable architectural metrics
- Directionality of imports
- Cyclomatic Complexity: where is edges, is nodes, and is connected components
- CRAP index
- Package dependency stats, characteristics, licenses, etc.
- Class dependencies
- Inheritance depth and structure
- Layer checks
- Put a fitness function on contentious things to free up the discussion. E.g., a "bad legacy system" might have fine reliability and capacity for your integration, so just use it and move on.
- Always use real user data for decisions about what people use and want.
- Use chaos engineering and real fitness functions to measure your DevOps proficiency
- Platform layer APIs (any really) could use things like pact (contract) tests to ensure they provide and consume consistent APIs with multiple producers and consumers.
- Avoid the Frozen Caveman Antipattern which is when your past problems, decisions, or mistakes are overvalued in your design of new systems. Just because you had that one freak outage one time doesn't mean it's likely to happen and needs to be over-engineered now.
- Recall the famous GitHub Scientist system that ran experiments in prod as a holistic, end to end fitness function that gauges a replacement's fitness to replace the original. Sometimes called a fidelity fitness function, these are similar to pinning tests.
- Automating fitness functions should be used as a checklist and a gradual guiding force, not a stick to beat people with.
- 202204021218 Architecture Decision Records should include a section about how it will be enforced with an automated fitness function.
Part II — Structure
Chapter 5 — Evolutionary Architecture Topologies
- 202606170922 Sapir-Whorf Hypothesis
- Connascence - similar to coupling. Two components are connascent if a change in one would require the other to be modified in order to maintain the overall correctness of the system. Originated from Meilir Page-Jones in What Every Programmer Should Know About Object-Oriented Design (1996).
- Static Connascence - source code level coupling, analyzable at compile time. These are things that multiple components must agree on if they are connascent in this way.
- Connascence of Name (CoN) - the name of an entity (function/class name)
- Connascence of Type (CoT) - the type of an entity (int vs string)
- Connascence of Meaning (CoM) - the meaning of values (true != false)
- Connascence of Position (CoP) - the order of values (
fn(int, int)swap) - Connascence of Algorithm (CoA) - the algorithm (TLS handshake)
- Dynamic Connascence - runtime coupling, harder to analyze, and deal with
- Connascence of Execution (CoE) - the order of execution (calling
sendbeforeto_address, but this example could be solved with state machines) - Connascence of Timing (CoT) - the timing of execution (race conditions)
- Connascence of Values (CoV) - the related change of values (change
Rectanglepoint without changing the others violatesRectangle) - Connascence of Identity (CoI) - the reference to an entity (invalid pointer, distributed system resource missing, etc.)
- Connascence of Execution (CoE) - the order of execution (calling
- Encapsulate, decouple, push for 202406282245 Locality of Behavior and strong, static type systems
- Static Connascence - source code level coupling, analyzable at compile time. These are things that multiple components must agree on if they are connascent in this way.
- 202511120846 Domain Driven Design's 202606170951 Bounded Context
- Architectural quanta are characterized by their static coupling (e.g. independent deployability, data storage), dynamic coupling (communication, consistency, coordination), and functional cohesion.
- We use contracts ranging from implicit unspoken agreements all the way to automatically verified (like Pact) to define how portions of our architecture connect with one another.
- Use sidecar containers for reusable operational concerns (logging, metrics)
Understanding the impact of structure on the ability to evolve a software system is key for architects. [...] Whether inspired by the locality property of connascence of bounded context in DDD, controlling the extent of implementation coupling is the key to building evolutionary architectures.1 (pp. 123)
Linked from this note
- 202109251100 Verify behaviors and assert properties
- 202204021218 Architecture Decision Records
- 202204272309 Modules should be loosely coupled
- 202205031449 Optimize for change
- 202406271256 Essential XP Emergent Design
- 202406282245 Locality of Behavior
- 202511120846 Domain Driven Design
- 202606021226 Fitness functions
- 202606170922 Sapir-Whorf Hypothesis
- 202606170951 Bounded Context