More on this book
Community
Kindle Notes & Highlights
by
Neal Ford
Read between
July 10, 2021 - September 10, 2023
A good example of Conway’s Law in action might be trying to change the contract between two services, which could be difficult if the successful change of a service owned by one team requires the coordinated and agreed-upon effort of another.
For example, in the case of architectural fitness functions, issues like performance might conflict with security due to the cost of encryption. This is a classic example of the bane of architects everywhere — the tradeoff.
Systemwide fitness functions allow architects to think about divergent concerns using the same unifying mechanism of fitness functions, capturing and preserving the important architectural characteristics.
Here is an example of a fitness function defined at the technical architecture dimension to control the directionality of coupling between components.
In the Java ecosystem, JDepend is a metrics tool that analyzes the coupling characteristics of packages. Because JDepend is written in Java, it has an API that developers can leverage to build their own analysis via unit tests.
Later, she realizes dateofbirth isn’t needed after all. She could just remove the 24th migration, and the end result on the table is no column. However, any code written between the time Danielle ran the migration and now assumes the presence of the dateofbirth column, and will no longer work if for some reason the project needs to back up to an intermediate point (e.g., to fix a bug). Instead, to remove the no-longer needed column, she runs a new migration that removes the column.
When architects think of migrating architecture, they typically think of the coupling characteristics of classes and components, but ignore many other dimensions affected by evolution, such as data.
Transactional coupling is as real as coupling between classes, and just as insidious to eliminate when restructuring architecture.
Architects aren’t immune to the “meta-work is more interesting than work” syndrome, which manifests in choosing inappropriate but buzz-worthy architectural styles like microservices.
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 interde...
This highlight has been truncated due to consecutive passage length restrictions.
Sharing is a form of coupling, which is highly discouraged in architectures like microservices. An alternative to sharing a library is replication, as illustrated
Because the framework calls the developer’s code, it creates a high degree of coupling to the framework.
Contrast that with library code, which is generally more utilitarian code (like XML parsers, network libraries, etc.) and has a lower degree of coupling.
In this case, PenultimateWidgets’ change wasn’t difficult from a data evolution standpoint because the developers were able to make an additive change, meaning they can add to the database schema rather than change it.
What about the case where the database must change as well because of a new feature?
To escape this antipattern, treat all software as just another integration point, even if it initially has broad responsibilities. By assuming integration at the outset, developers can more easily
replace behavior that isn’t useful with other integration points, dethroning the king.
Because they require huge investments of both time and money, companies are reluctant to admit when they don’t work.
Microservices eschew code reuse, adopting the philosophy of prefer duplication to coupling: reuse implies coupling, and microservices architectures are extremely decoupled.
Reporting is a good example of inadvertent coupling in monolithic architectures. Architects and DBAs want to use the same database schema for both system of record and reporting, but encounter problems because a design to support both is optimized for neither.
Many microservices architectures solve the reporting problem by separating behavior, where the isolation of services benefits separation but not consolidation.
Architects commonly build these architectures using event streaming or message queues to populate domain “system of record” databases, each embedded within the architectural quantum of the service, using eventual consistency rather than transactional behavior.
A set of reporting services also listens to the event stream, populating a denormalized reporting dat...
This highlight has been truncated due to consecutive passage length restrictions.
As developers learn to utilize these tools to support software development, we envision fitness functions based on AI that look for anomalous behavior.
Traditional unit tests include assertions of correct outcomes within each test case.
Alternatively, evolving implies fundamental change. Building an evolvable architecture entails changing the architecture in situ, protected from breakages via fitness functions.