Skip to content
back to the episode
Episode summary2022Fellow

Software Architecture: The Hard Parts — Episode 4

The fourth episode opens the book's second part — how to put the pieces back together. Ilya, Alexander, and Vova Chistyakov cover the chapters on code reuse and data ownership: copy-paste, shared libraries, shared services, and sidecars with a service mesh, Uncle Bob's component principles, who writes to a shared table, ACID versus BASE, and three ways to reach eventual consistency.

Code of Architecture · Book Club8 min read

This summary is shorter than the full episode; examples and assessments reflect the participants’ experience and views.

The main thread of the material
01

Four ways to reuse code

The chapter starts with DRY versus WET and four approaches: code replication, shared libraries, shared services, and sidecars. Replication is copy-paste with a nice name: the context is maximally isolated, nothing is shared, but changes cannot be distributed or versioned; Vova finds the authors' example with annotations odd. Alexander recalls his first job, where a website had fifty variations of date handling and fixing one left the rest untouched — after which he read Code Complete. Shared libraries solve versioning and catch errors at compile time, but bring dependency management, duplication per language in a heterogeneous environment, and the question of stale versions: the strategy for supporting old versions depends on release frequency, as with Linux distributions, and in a distributed system it is unclear whom to tell about a new version.

Alexander supplements the chapter with Uncle Bob's component principles from Clean Architecture, which he considers a stronger analysis than the authors'. Release/Reuse Equivalence — a component is released in versions, usually by SemVer; Common Closure — a component gathers what changes for the same reason at the same time and separates what changes for different ones; Common Reuse — do not make users depend on what they do not use, a continuation of interface segregation. The principles form a "pick two of three" triangle: forget reuse and the library is hard to reuse and navigate; forget closure and every change stretches across many components; forget release and the all-inclusive glued component ships every three hours, consumers read the changelog and eventually stop updating. The book's example — a small component changed and only two services affected instead of all — is exactly an illustration of Common Reuse.

02

Shared services, sidecars, and a stable abstraction

Ilya treats the shared service as the answer to heterogeneity: one implementation of the logic behind a contract instead of libraries per language, and one deploy instead of distributing versions. The price: errors move from compile time to runtime, API versioning with the question of how many versions to support, fault tolerance because everyone loses the logic when the service is down, performance bounded by the network, and the need to scale that service separately. Ilya recalls a project that used all three approaches at once and collected every drawback: hard-to-distribute copy-paste, frequently updated libraries, and runtime errors of the service. The authors explain sidecars weakly, so Alexander goes through Kubernetes Patterns: the basic unit is a pod of several containers; an init container prepares the application to start, a sidecar extends it without changes — composition over inheritance, for example intercepting logs from a file into stdout; an adapter unifies a heterogeneous system, an ambassador cuts the application off from external dependencies, like ports and adapters.

The point of a sidecar is that it is a separate process on the same host: you reach it over localhost, yet there is neither the library's version-distribution problem nor the shared service's network latency, and the platform team updates it — that is how a service mesh works, with a control plane and a data plane in front of every service. Vova specifies: the authors propose sidecars for operational matters — logging, monitoring — not for domain libraries, which the book's heroes resisted. Alexander adds the chapter's orthogonal dependencies: the sidecar separates the operational axis from the business logic. His main conclusion goes beyond the book: reuse works when the abstraction is stable, and at a high rate of change — in the core domain — a shared solution brings pain; platform teams and "platform as a service" are no silver bullet, and if the platform moves like an elusive target, its users will be uncomfortable.

03

Who owns the data and how to reach consistency

The data ownership chapter: if one service writes to a table — single ownership, no questions. Common ownership — almost everyone writes, as in audit; the answer is a dedicated owner service to which the others send events asynchronously, via Kafka if need be. Joint ownership — several services of one domain write different fields, like catalog and inventory into the product table, and there are four techniques. Split the table in two if consistency is not critical. Data domain — leave the shared schema to both, and Alexander recalls that in Enterprise Integration Patterns integration through a database is an anti-pattern where a schema change breaks who knows whom; two services on a shared database is bad, three very bad, four beyond that. Delegate ownership to one: by primary domain priority — the one closer to the data structure that already has an API for it, like catalog; or by operational characteristics — the one that updates more often, like inventory; the authors prefer the first with a cache for speed, Alexander would look at the SLAs. And consolidation — merge the services back: transactions out of the box, but a shared failure domain.

Then distributed transactions: Vova recaps ACID — atomicity, consistency, isolation of concurrent users, durability after a pulled plug — and BASE from the NoSQL world: basic availability of the participants, soft state visible half-way, and eventual consistency — leave the system alone and it converges, the only question is over what horizon. Three patterns to get there. Background synchronisation: a separate process copies data across other services' tables; Ilya compares it to Kleppmann's anti-entropy, but Alexander sees the problem — it is no longer an infrastructure mechanism but a process that knows everyone's domain semantics, policy mixed with mechanism. The orchestrated request: within a client's request visit every service and reply once everything is updated; error handling and call order get harder, and availability — Alexander computes on the fly — multiplies: three services at 99.9% give about 99.7%. Event-based: a service publishes an event and the rest subscribe — the approach the club studied with Kleppmann and Vlad, although the authors blur events and commands. The next guest is Igor Maslov, head of core technologies.

Takeaways

What to take away

  1. 01Copy-paste isolates the context but cannot be versioned; a shared library catches errors at compile time but needs a deprecation strategy and duplication per language; a shared service moves errors to runtime and is bounded by the network.
  2. 02Uncle Bob's component principles — Release/Reuse, Common Closure, Common Reuse — form a "pick two" triangle and make a stronger analysis than the authors'; reuse only works on a stable abstraction.
  3. 03A sidecar is a separate process on the same host run by the platform team: no library distribution and no network latency, but for operational concerns, not domain logic.
  4. 04A table shared by several services is integration through the database; better delegate ownership to one service by domain proximity or SLA, and reach eventual consistency with events rather than orchestration that multiplies the nines away.

Sources