More on this book
Community
Kindle Notes & Highlights
by
Neal Ford
Read between
December 25 - December 29, 2017
An evolutionary architecture consists of three primary aspects: incremental change, fitness functions, and appropriate coupling.
Unlike the more directed fitness functions, architects will likely never try to “evaluate” this systemwide fitness function. Rather, it provides guidelines for prioritizing decisions about the architecture in the future.
Atomic fitness functions run against a singular context and exercise one particular aspect of the architecture.
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.
Monitoring-driven development (MDD) is another testing technique gaining popularity.
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.
While most fitness functions trigger on change, architects may want to build a time component into assessing fitness. For example, if a project uses an encryption library, the architect may want to create a temporal fitness function as a reminder to check to see if important updates have been performed.
While architects will define most fitness functions at project inception as they elucidate the characteristics of the architecture, some fitness functions will emerge during development of the system.
Some architectures have specific concerns, such as special security or regulatory requirements.
Teams should identify fitness functions as part of their initial understanding of the overall architecture concerns that their design must support. They should also identify their system fitness function early to help determine the sort of change that they want to support.
A fitness function review is a meeting with key business and technical stakeholders with the goal of updating fitness functions to meet design goals.
Microservices implement a share nothing architecture: Each service is operationally distinct to eliminate technical coupling and therefore promote change at a granular level.
the modern process of hypothesis-driven development. Under this process, rather than gathering formal requirements and spending time and resources building features into applications, teams should leverage the scientific method instead.
To learn from users, the developers released a new version of the legacy application with logging enabled to track which menu features users actually used.
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.
Domain-driven design (DDD) is a modeling technique that allows for organized decomposition of complex problem domains. DDD defines the bounded context, where everything related to the domain is visible internally but opaque to other bounded contexts.
In evolutionary architecture, architects deal with architectural quanta, the parts of a system held together by hard-to-break forces.
If you can’t build a monolith, what makes you think microservices are the answer? Simon Brown
One of the major factors that impacts the ability to evolve an application at the architectural level is how unintentionally coupled each part of the system is.
In physics, the strong nuclear force that binds atoms together is one of the strongest forces yet identified. Transactional contexts act like a strong nuclear force for architecture quanta.
Database transactions act as a strong nuclear force, binding quanta together.
While systems often cannot avoid transactions, architects should try to limit transactional contexts as much as possible because they form a tight coupling knot, hampering the ability to cha...
This highlight has been truncated due to consecutive passage length restrictions.
Refusing to refactor schemas or eliminate old data couples your architecture to the past, which is difficult to refactor.
the three primary aspects of evolutionary architecture—fitness functions, incremental change, and appropriate coupling—separately.
Understand the business problem before choosing an architecture.
If developers cannot easily deploy changes, all parts of the feedback cycle are hampered.
Don’t build an architecture just because it will be fun meta-work.
When restructuring architecture, consider all the affected dimensions.
Splitting the architecture into domains, along with better team structure and operational isolation, allows for easier incremental change, one of the building blocks of evolutionary architecture, because the focus of work matches the physical work artifacts.
When decomposing a monolithic architecture, finding the correct service granularity is key. Creating large services alleviates problems like transactional contexts and orchestration, but does little to break the monolith into smaller pieces. Too-fine-grained components lead to too much orchestration, communication overhead, and interdependency between components.
developers build the infrastructure anew each time the deployment pipeline executes, catching breaking changes as aggressively as possible.
Identify and remove needless variability.
Build just-in-time anticorruption layers to insulate against library changes.
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
Update framework dependencies aggressively; update libraries passively.
When versioning services, prefer internal versioning to numbering; support only two versions at a time.
Architects define invariants at key integration points as fitness functions, which run as part of a deployment pipeline, ensuring abstractions don’t start to leak in undesirable ways.
The more reusable code is, the less usable it is.
Microservices eschew code reuse, adopting the philosophy of prefer duplication to coupling: reuse implies coupling, and microservices architectures are extremely decoupled. However, the goal in microservices isn’t to embrace duplication but rather to isolate entities within domains.
When coupling points impede evolution or other importance architectural characteristics, break the coupling by forking or duplication.
Unless, of course, the goal of the architecture is the Resume-Driven Development pitfall—utilizing every framework and library possible to tout that knowledge on a resume.
Don’t build architecture for the sake of architecture—you are trying to solve a problem.
Goldilocks Governance model works well: pick three technology stacks for standardization—simple, intermediate, and complex—and allow individual service requirements to drive stack requirements.
Speed of evolution is a function of cycle time; faster cycle time allows faster evolution.
In software, this is seen in the form of the irrational artifact attachment—the more time and effort you invest in planning or a document, the more likely you will protect what’s contained in the plan or document even in the face of evidence that it is inaccurate or outdated.
Organize teams around business capabilities, not job functions.
One mechanism many companies use to shift their team emphasis is to model their work around products rather than projects.
Strive for a low number of connections between development teams.