Long-form notes on software, distributed systems, and the craft of building.
One post, every Monday. Search or browse the archive below.
Database per Service Is Free Now. That Is Not a Reason to Use It
A free tier that doubles changes the inputs to a decision, not the decision. In week one I predicted Bullpen would split prices and history into two services with two databases. Then Neon made database per service look free, and I had to decide whether that changed anything. This is the decision history: five data and deployment decisions, each with its conditions, the options I weighed, why one won, and what would make me reverse it.
Architecture as Code Has Three Jobs: Describe, Govern, Remember
In Bullpen's first week, the planned architecture model still named a stock provider I had turned down, and a footer change and a test followed it. Nothing failed. I use that incident and two others to argue that architecture as code has three jobs: describe the system, govern it, and remember why it looks the way it does. Then I follow the first real change after the fixes, product analytics, through all three: a decision record before the model could name it, built and planned architecture that disagreed, and a rule that composes the two instead of editing either.
When to Use Microservices in 2026: Scaling Alone Is No Longer the Reason
On the day the Dow posted a then-record gain, Robinhood's customers could not trade from the open to the close. Splitting out the hot part so it scales alone was the old answer; a serverless platform now does that per function. I open this series with Bullpen, a paper-trading league on live market data, and argue when to use microservices in 2026: only for a reason I can name, with a check, a measurement or a bill behind it.
The Comprehension Gate: Making Human Understanding an Engineering Constraint
The last post introduced comprehension coverage as a concept. This is the field report: the instrument exists, it is open source, and its first run was on a repository I own — a small library developed predominantly through coding agents. Four of six modules came back dark. The only entity the instrument recognizes as a comprehender of the signature module is a bot. This post walks the real map, the calibration run that argued with the spec and won, the trace of a dark module, the two failures the first run was required to produce, and the gate a CI pipeline can now enforce — with a break-glass valve whose every use feeds the map. The first five posts made the human layer explicit. This one makes it enforceable.
The Jev AI Model Can't Hallucinate. It Still Got 37% of Emails Wrong.
The Jev AI model cannot return an answer outside the schema you give it, and launch coverage turned that into a promise that it cannot hallucinate. On an independent phishing benchmark, the same model got 37.4% of 2,000 emails wrong when asked one broad question, while five narrow questions to it, combined in code by a cross-validated logistic regression, reached 95.1%. I read TypeSafe's docs and evals, ran the Pydantic AI adapter, and mapped what Jev was built for, where the launch claims hold, and where I would and would not put it in a pipeline.
Introducing Comprehension Coverage
Every codebase carries two maps: what the tests verify, measured to two decimals, and what the humans still understand, measured by nobody. For twenty years the second map came free with git blame — authorship implied understanding. Agents deleted that axiom. Starting from Naur's forty-year-old warning about programs dying with their theory, through the forgetting-curve research and a 2026 preprint arguing the entire authorship-metric family collapsed at once, these are my notes introducing comprehension coverage: a per-module, per-person, evidence-based, churn-decaying map of which humans still understand which parts of a system — with its closest prior art named, its refutation conditions attached, and the instrument that builds it coming next.
The Bottleneck Moved to Review — and Nobody Agrees on What Happens Next
The sharpest objection to "read every diff" is arithmetic: agents write faster than humans read. My answer is to stop arguing about review and name what review actually was — the profession's control point, the one place where observation, decision, and enforcement coincided. That point is saturating, and a saturated control point does not slow a system down; it gets bypassed and turns nominal. These are my notes on what a control point requires to function, why the arithmetic broke this one, and how each industry response — agents reviewing agents, verify-don't-review, risk-routing — is really a relocation that gives up a different property. No answer at the end. Three questions instead.
Fitness Functions Are the Control Plane for Agentic Coding
The last post asked where the control point went. Here is the first answer I am willing to defend: it did not go away — part of it compiled. Architectural fitness functions, pointed at coding agents, become the control plane that lets a developer stay in charge without becoming the bottleneck: judgment compiled once into deterministic gates that enforce at machine speed, with failure messages written as prompt engineering for the retry loop. Then the complication that shapes the whole post: the moment an agent optimizes against the compilation, the compiled control becomes an object of attack — and fitness function design inherits an arms race, with a Kotlin/ArchUnit constitution to make it concrete.
Stamp vs Control Coupling: Where Does the Write Decision Live?
Control coupling and stamp coupling sit side by side in the Constantine taxonomy, ranked worse than data coupling and not much else. When I re-read it with a real Kotlin applyDiscount refactor in front of me, the ranking was not the interesting part. The question it does not answer is where the write decision actually lives.
The Retry That Outlived Its Token: Temporal Fakes in Go
I once shipped a test that mocked a token as always valid, and it hid a retry that reached the provider after the token expired. These are my notes on rebuilding that test in Go with a temporal fake — a stateful provider that shares a clock with the code — plus testing/synctest, so a 30-second TTL runs in microseconds and the race finally turns red.