Monday, October 10, 2022

Building Evolutionary Architectures

2022-building-evolutionary-architectures

Notes for the book Building Evolutionary Architectures by Neal Ford, Rebecca Parsons and Patrick Kua.

Main take-aways / Summary

  • Software architectures are not created in a vacuum - They always reflect the ecosystem in which they were defined
    • E.g. When SOA was popular, all infrastructure was commercial, licensed and expensive.
  • An evolutionary architecture supports guided, incremental change across multiple dimensions.
  • Anything that verifies the architecture is a fitness function
    • -> Treat those uniformly
    • Think of architectural characteristics as evaluable things.

Software Architecture

  • There are many definitions for software architecture.
  • There are many "-ilities" for software architecture to support. In this book adding a new one: evolvability.
  • Whatever aspect of software development - we expect constant change.
  • Alternative to fixed plans? Learning to adapt. Make change less expensive e.g. by automating formerly manual processes etc.
  • Yet another definition of software architecture: "parts hard to change later"
    • Convenient definition but blind spot as a potentially self-fulfilling prophecy.
  • -> Building changeability into architecture?
    • Having ease of change as a principle.

Evolutionary architecture

  • Book's definition:

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

  • Incremental change - Two aspects: How teams build software incrementally and how they deploy it.
  • Guided changes - Once architects have chosen important characteristics, they want to guide changes to the architecture to protect those characteristics.
  • There are many dimensions of architecture
    • Architectural concerns, i.e. the list of "-ilities".
    • Not only "-ilities" but other dimensions to consider for evolvability
      • Technical dimensions
      • Data
      • Security
      • Operational/System
  • There are various techniques for carving up architectures
  • In this book, in contrast, we don't attempt to create a taxonomy of dimensions but rather recognize the ones extant in existing projects.
  • Impact of team structure on surprising things, e.g. architecture -> Conway's law

Organizations which design systems ... are constrained to produce designs which are copies of the communication structures of these organizations.

  • So one should not pay attention only to the architecture and design of the software, but also the delegation, assignment, and coordination of the work between teams.
  • Inverse Conway Maneuver - Structuring teams and organizational structure around the desired architecture.

Structure teams to look like your target architecture, and it will be easier to achieve it.

  • Two critical characteristics for evolutionary architecture: incremental and guided.

Fitness Functions

  • Book's definition for a fitness function:

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

  • Systemwide fitness function - a collection of fitness functions corresponding for different dimensions of the architecture.
  • It is an important architectural decision to define important dimensions (scalability, performance, security, ...)
  • Different "categories" of fitness functions
    • Atomic vs Holistic
    • Triggered vs Continual
    • Static vs Dynamic
    • Automated vs Manual
    • Temporal (e.g. "break upon upgrade"
    • Intentional over Emergent (There will usually be unknown unknowns)
    • Domain-specific
  • Fitness function categories to classify them into
    • Key - Crucial ones
    • Relevant
    • Not relevant

Engineering Incremental Change

Architecture is abstract until operationalized, when it becomes a living thing

  • Long-term viability of an architecture cannot be judged until design, implementation, upgrade and inevitable change are successful.
  • Common combinations of fitness function categories
    • atomic + triggered - e.g. unit tests
    • holistic + triggered - e.g. wider integration testing via a deployment pipeline
    • atomic + continual
    • holistic + continual - e.g. Chaos Monkey
  • Hypothesis- and Data-Driven Development
    • Hypothesis-driven development - Include users also in the feedback loop

Architectural Coupling

  • Focusing appropriate coupling - how to identify which dimensions of the architecture should be coupled
  • Term definitions here
    • module - some way of grouping related code together
    • modularity - logical grouping of related code
    • components - physical packaging of modules
    • Modules imply logical grouping while components imply physical grouping.
    • library is one kind of a component
  • functional cohesion - business concepts semantically binding parts of the system together
  • architectural quantum - independently deployable component
    • quantum size determines the lower bound of the incremental change possible
  • One key thing: Determining structural component granularity and coupling between components
  • In general, the smaller the architectural quanta, the more evolvable the architecture will be.
  • JDepend for package dependencies

Evolvability of architectural styles

  • Different architectural styles have different inherent quantum sizes

Big Ball of Mud

  • Quantum: The whole system
  • Increment change is difficult because of scattered dependencies
  • Building fitness functions is difficult because no clearly defined partitioning
  • Good example of inappropriate coupling

Unstructured monoliths

  • Large quantum size hinders incremental change
  • Building fitness functions difficult but not impossible
  • Somewhat similar coupling as with Big Ball of Mud

Layered monoliths

  • Quantum is still the whole application
  • Incremental change easier particularly if changes are isolated to existing layers
  • Easier to write fitness functions with more structure
  • Often easy understandability

Modular monoliths

  • Many of the benefits of microservices can be achieved also with monoliths if developers are extremely disciplined about coupling
  • Incremental change easier because of modularity
  • Easier to design and implement fitness functions
  • Appropriate coupling

If you can't build a monolith, what makes you think microservices are the answer (Simon Brown)

Microkernel

  • Commonly used in e.g. browsers and IDEs
  • Typically a core system with an API for plug-ins
  • Quantum: One for the core, another for the plug-ins

Event-Driven architectures - Broker pattern

  • Typically message queues, initiating event, intra-process events, event processors
  • Coordination and error handling typically difficult
  • Allow incremental change in multiple forms
  • Atomic fitness functions typically easy to write but holistic fitness functions are both necessary and complex in this architecture
  • Low degree of coupling - Between services and the message contracts

Event-Driven architectures - Mediator pattern

  • Has a hub that acts as a coordinator
  • Primary advantage: Transactional coordination
  • INcremental change as with the broker pattern
  • Holistic fitness functions easier to build than with the broker version
  • Coupling increases

Broker or mediator - classic example of an architectural tradeoff

Service-Oriented Architectures - ESB-driven SOA

  • Enterprise Service Bus (ESB) - Mediator for event interactions
  • Style differs but is based on segregating services based on reusability, shared concepts and scope.
  • Architectural quantum is massive - Entire system
  • Incremental change allows reuse and segregation of resources but hampers making the most common types of change to business domains.
  • Testing in general is difficult.
  • Note: Software architectures are not created in a vacuum - They always reflect the ecosystem in which they were defined
    • E.g. When SOA was popular
      • Automatic provisioning of machines wasn't possible
      • all infrastructure was commercial, licensed and expensive.

Service-Oriented Architectures - Microservices

  • Combines engineering practices of Continuous Delivery with logical partitioning of bounded contexts
    • Typically separated along domain dimension
    • Compared to typical layered architecture, a microservice has all the layers but handles only one bounded context
  • 7 principles from Building Microservices
    • Modelled around the business domain
    • Hide implementation details
    • Culture of automation
    • Highly decentralized
    • Deployed independently
    • Isolate failure
    • Highly observable
  • "Share nothing" - "No entangling coupling points"
  • Service templates as DropWizard and Spring Boot
  • Why not done before? See the earlier not on e.g. SOA

Evolutionary standpoint:

  • Supports both aspects of incremental change
  • Easy to build both atomic and holistic fitness functions.
    • (Well, I wouldn't agree 100% myself about holistic fitness functions)
  • Two kinds of coupling: Integration and service template

Service-based architectures

  • Similar to microservies but differ in one or more:
    • service granularity - bigger services / quantum size
    • database scope - sharing a database
    • integration middleware - a mediator like service bus
  • Incremental change relatively functional
  • Potentially more difficult to write fitness functions
  • More coupling

"Serverless" Architectures

  • Broadly, two different meanings
    • BaaS - Backend as a Service
    • FaaS - Function as a Service
  • Supports incremental change
  • Typically requires more holistic fitness functions
  • Attractive because it eliminates several dimensions/concerns
  • Suffers from serious constraints also

Evolutionary Data

Migrations

  • Developers should treat changes to database structure the same way as to source code: tested, versioned and incremental
  • Most teams have moved away from building undo migration capabilities
    • If all the migrations exist, database can be built just to the point they need without backing up to a previous version
    • Why maintain two versions of correctness, both forward and backward?
    • Sometimes daunting challenges / impossible (e.g. dropping a column or a table)

Shared Database integration

  • Shared Database Integration pattern
  • Using the database as an integration point fossilizes the database schema across all sharing projects
  • To evolve the schema: Expand/contract pattern
  • Options on example change
    • No integration points, no legacy data -> straight-forward
    • Legacy data, no integration points -> migrate the data, after that done
    • Existing data and integration points -> Potentially DB triggers etc.

Two-phase commit Transactions

  • Transactions are a special form of coupling because transactional behavior doesn't typically appear in traditional architecture-centric tools
  • Heavily transactional systems difficult to translate to e.g. microservices
  • Binding by databases is imposing because of transaction boundaries, which often define how the business processes work.

Database transactions act as a strong nuclear force, binding quants together.

Age and Quality of Data + summary

  • adding another join table is a common process used for t expand schema definitions
  • For evolutionary architecture, make sure developers can evolve the data as well (both schema and quality)

Refusing to refactor schemas or eliminate old data couples your architecture to the past, which is difficult to refactor.

Summary

  • The database can evolve alongside the architecture as long as proper engineering practices are applied, such as continuous integration, source control etc.
  • Refactoring databases in an important skill and craft.

Building Evolvable Architectures

  • Tieing previously handled aspects together (fitness functions, incremental change and appropriate coupling)

Mechanics

  • Identify Dimensions Affected by Evolution
  • Define Fitness Function(s) for Each Dimension
  • Use Deployment Pipelines to Automate Fitness Functions

Retrofitting existing architectures

  • Three factors
    • Component coupling and cohesion
    • Engineering practise maturity
    • Developer ease in crafting fitness functions
  • Refactoring vs Restructuring
    • Refactoring - No changes to external behavior
    • Restructuring an architecture - Often changes also behavior
  • Migrating architectures
    • Architects are often tempted by highly evolutionary architecture as a target for migration but this is often difficult, mainly because of existing coupling.
    • Trap of Meta-work is more interesting than work (rather writing a framework than using a framework)

Don't build an architecture just because it will be fun meta-work.

When restructuring architecture, consider all the affected dimensions.

Migrating Architectures

  • When decomposing a monolithic architecture, finding the correct service granularity is key.
    1. Partitioning - considering
    • Business functionality groups
    • Transactional boundaries
    • Deployment goals
    1. Separation of business layers from the UI
    1. Service discovery

When migrating from a monolith, build a small number of larger services first. (Sam Newman)

Various Guidelines for building Evolutionary Architecture

All architectures become iterative because of unknown unknowns, Agile just recognizes this and does it sooner.” (Mark Richards)

  • Build Anticorruption Layers
    • Encourages one to think about the semantics of what is needed from a library, not the syntax.

Developers understand the benefits of everything and the tradeoffs of nothing! (Rich Hickey)

Service Templates

  • Remove needless variables
  • Services templates are one common solution for ensuring consistency
    • Pre-configured sets of common infrastructure libraries (logging, monitoring, ...)
  • Seen as appropriate coupling by the book.

Build Sacrificial Architectures

The management question, therefore, is not whether to build a pilot system and throw it away. You will do that. […] Hence plan to throw one away; you will, anyhow. (Fred Brooks)

Mitigate External Change

  • When relying on code from a third party, create own safeguards against unexpected occurrences: breaking changes, unannounced removal, and so on

Transitive dependency management is our "considered harmful" moment (Chris Ford)

Updating Libraries vs Frameworks

  • "a developer's code calls library whereas the framework calls a developer's code"
  • Libraries generally form less brittle coupling points than frameworks.
  • One informal governance model treats framework updates as push updates (~ASAP) and library updates as pull updates ("update when needed")

Various

  • Version numbering vs internal resolution
    • Prefer internal versioning to numbering
    • support only two versions at a time

Evolutionary Architecture Pitfalls and Antipatterns

  • Pitfalls and antipatterns
    • An antipattern is a practice that initially looks like a good idea, but turns out to be a mistake
    • A pitfall looks superficially like a good idea but immediately reveals itself to be a bad path

Antipattern: Vendor King

  • To escape: Treat all software as just another integration point

Pitfall: Leaky Abstractions

All non-trivial abstractions, to some degree, are leaky (Joel Spolsky)

Antipattern: Last 10% Trap

  • Experiences from a project with 4GL (rapid application development tools)
    • 80% of the functionality was quick and easy to build
    • Next 10% was extremely difficult but possible
    • Last 10% wasn't achieved
  • IBM's San Francisco Project
    • infinite regress problem

Antipattern: Code Reuse Abuse

Software reuse is more like an organ transplant than snapping together Lego blocks. John D. Cook

  • Ease of code use is often inversely proportional to how reusable that code is.
  • Microservices might adtop the philosophy of prefer duplication to coupling

When coupling points impede evolution or other important architectural characteristics, break the coupling by forking or duplication.

Antipattern: Inappropriate Governance

  • Software architecture never exists in a vacuum but it's often a reflection of the environment in which it was designed
  • Goal in most microservices projects isn't to pick different technologies cavalierly, but rather to right-size the technology choice for the size of the problem.
  • Goldilocks Governance model: Pick three technology stacks for standardization: Simple, intermediate and complex

Pitfall: Planning Horizons

  • The more time and effort you invest in planning or a document, the more likely you will protect what's contained in the plan/document even when it is inaccurate or outdated.

Putting Evolutionary Architecture into Practice

  • Cross-Functional Teams
    • One goal here is to eliminate coordination friction
  • Organize teams around business capabilities, not job functions
  • Product over Project
    • Products live potentially forever, unlike the lifespan of project
    • Inverse Conway Maneuver
  • Dealing with external change: Consumer-driven contracts
  • Culture
    • Adjusting the behavior of a team often involves adjusting the process around it

Tell me how you measure me, and I will tell you how I will behave. (Dr Eliyahu M. Goldratt / The Haystack Syndrome)

  • Culture of Experimentation

The real measure of success is the number of experiments that can be crowded into 24 hours. (Thomas Alva Edison)

  • Finding the sweet spot between the proper quantum size and the corresponding costs.
  • The role of an Enterprise architect (in an evolutionary architecture): Guidance and enterprise-wide fitness functions

Why should a Company choose to build an Evolutionary Architecture? (A bit of a last chapter sales pitch)

  • Predictable vs evolvable
  • Scale
  • Advanced business capabilities
  • Cycle time as a business metric
  • Isolating architectural characteristics at the quantum level

Why Should a Company Choose Not to build an Evolutionary Architecture?

  • Can't evolve a ball of mud
  • Other architectural characteristics dominate
  • Sacrificial architecture