Hoare's quote and the kinds of complexity
The guest opens with a disclaimer: he has not finished the book yet and will return to it at a more relaxed pace, so Alexander drives the discussion while he shares experience and thoughts. The book intrigued him, and he puts it next to Martin Kleppmann's Designing Data-Intensive Applications: both can be read at any level, and a mid-level engineer, a senior and a lead will each find something new. There is plenty of overlap with Uncle Bob's Clean Code, but this one, in Grisha's words, is more of a lead's book: higher-level system design and how to run the engineering process inside a team. Alexander came to it two or three years ago after hearing Vlad Khononov talk about microservices and DDD and mention deep and shallow modules. The author, John Ousterhout, teaches at Stanford and is one of the creators of a consensus algorithm meant as a more understandable replacement for Paxos. The book has an introduction and 22 chapters over 190 pages; Grisha knows of no Russian translation.
The book grew out of a course: Ousterhout set his students project assignments, they built several projects in teams over a semester, and he gave them feedback. A course does not scale — it needs someone to review the work — whereas a book broadcasts ideas worth spreading. Unlike Uncle Bob, many of whose theses hardened into dogma and some of which no longer hold, the advice here is practical: discussing Go, the author says to live in Rome as the Romans do and adapt to the context that already exists. Chapter one explains the plan: first understand what kinds of complexity exist, then decide what to do about them. It also carries Tony Hoare's quote on the two methods of design — make a program so simple that there are obviously no errors, or so complicated that there are no obvious errors. Chapter two breaks complexity into its symptoms: change amplification, cognitive load and unknown unknowns.
Duplication, a Legacy module and tech debt
Those symptoms have two causes: excess dependencies and obscurity. Obscurity is when the information you need is unavailable and no mental model forms: you are told you did it wrong and ask where you were supposed to learn about an unwritten rule passed by word of mouth inside a circle of insiders. The author treats this with comments; Grisha treats it with conventions — he took the practice of developer books from Yandex into the companies he joined afterwards, and written-down processes of the sort "if you work with Kafka, do this" speed up onboarding and make rotations painless. A separate argument is about duplication: developers dislike it, but the shared part extracted into a library becomes the module everyone depends on, it has many reasons to change and they pull in different directions, and teams start wanting to fork it. As Alexander recalls it, the author allows duplicating a small piece when it is clear it will evolve in a different direction.
Grisha adds: treat a module as an API and always know whether your change is backward compatible. His company bumps its libraries almost monthly, and after an incompatible change landed, the team created a separate module called Legacy: old submodules go there, the name is an eyesore, and a backlog ticket appears to get rid of it one day. Working code is not enough for another reason too: in tactical mode you feel clever at first, then each increment gets smaller until technical bankruptcy, when it is easier to start version two. Business often needs speed — in Grisha's startup mobile hypotheses were built as fast as possible, but that code was never left in place, it was rewritten in full. Alexander brings a fresh story about an audio systems maker that rebuilt its mobile application for a new product line: people inside warned that twenty years without investment in updates made it impossible, the warning signals were ignored for the deadline and the investors, and the release brought bugs, functionality missing on some devices and an internal audit. Grisha's remedy is a numeric threshold: the sprint share given to debt grows from 20 to 40 per cent.
Deep modules and leaking abstractions
A module is an interface plus an implementation, and a narrow, consistent interface limits the reasons it will change. A deep module hides complexity: an ordinary engineer need not know how object storage such as S3 works inside, given an SLA on volume and speed. The book's counter-example is integration through a shared database: the interface is everything the database allows, nothing is hidden, and there is effectively no module. An interface should do the minimum needed — Grisha offers his outbox, where the interface owns cron jobs while the locks and records stay inside — though with a scattering of tiny functions you end up assembling the whole in your head. General-purpose modules are deeper than specialised ones: the author derived this from student projects (Alexander recalls they were on a simple text editor), and Grisha keeps the common tracing core in its own module that the Spring and Micronaut implementations depend on.
Information hiding breaks down when an abstraction leaks. Alexander recalls early Mongo, which delivered its promised speed by not syncing writes to disk and living without transactions, so data was lost; Grisha recently met transactions that do not work the way you expect. His own leak is an endpoint you can only call once you know the chain of calls before it; the cure is a state machine returning a meaningful error. Alexander's example is nastier: a common API built over boxed systems like a sarcophagus over Chernobyl, with caches warmed when the mobile app hits the home screen; a user arriving from an email straight into the method they need warms nothing, and the requests hammer the internal systems. In the chapter on levels of abstraction, a method that only passes a call through is a red flag, but Grisha argues back: an order service that looks empty and merely calls the repository later grows notifications and atomicity. Alexander answers with his own story of stuffing Gang of Four patterns everywhere after a certification. Seven chapters in an hour — and an agreement to meet again from chapter eight.
What to take away
- 01Shared code does not have to live in a library: a shared module has many reasons to change and they pull in different directions, so, by Alexander's recollection, the author allows duplicating a small piece that will drift apart in meaning.
- 02Tech debt is easier to hold in numbers: a separate Legacy module with a backlog ticket, numeric prioritisation, and a threshold above which a sprint's share for debt grows from 20 to 40 per cent.
- 03A module's depth is measured by how much it hides: object storage with a promised SLA is a deep module, while integration through a shared database leaves you with no interface at all.
- 04A method that only passes a call through is a red flag for abstraction levels, yet a wrapper service that looks empty often becomes the place for notifications and atomicity, and adding it later costs more.