Excavating Legacy ETL

Excavating Legacy ETL: The AI Never Asserts a Fact It Could Look Up

Every platform modernisation programme has the same first act, and it is never the interesting one. Before anyone gets to write a line of dbt, somebody has to work out what the current thing actually does. In the SAP BusinessObjects Data Services case — BODS, if you’ve had the pleasure — that means inheriting a few hundred dataflows whose authors left the organisation years ago. The logic lives in exported XML. The business rationale lived in the heads of people who are now at other companies. Nobody can tell you which flows are duplicates, which ones nothing reads any more, or what any given one is for. There is a spreadsheet somewhere claiming to be an inventory. It is wrong in ways nobody can enumerate. ...

July 21, 2026 · 16 min · Justin
Four Ways the Outbox Bit Me

Four Ways the Outbox Bit Me

In the previous post I described an architecture where the Moments library doesn’t know that sync exists. Services record Mutation values into a one-method trait; one implementation writes to an outbox table, another does nothing. The library code is identical regardless of which backend it’s running under, and a third backend could be added without touching the library at all. The architecture is sound. I still believe in it. The first six weeks of running it were a different story. Four bugs in particular are worth describing, because each one tells you something the diagrams don’t. Two are about the abstraction — assumptions the trait quietly makes about how the world works, and what breaks when the world doesn’t cooperate. Two are about the substrate — the boring queue-and-retry machinery underneath the trait, which has its own opinions about how things should be done. Clean architecture doesn’t relieve you of the substrate’s problems. It just gives you one place to deal with them. ...

May 31, 2026 · 12 min · Justin
Recording Mutations, Not Events

Recording Mutations, Not Events

Half of the library code in Moments, my photo manager, calls a one-method trait after every database write. That trait has two implementations. One of them returns Ok(()) and does nothing else. From inside the library, there is no way to tell which one is wired up. That is the entire architecture of how Moments syncs to Immich — and the entire reason the local backend, which has no sync at all, is the same program as the Immich one. ...

May 17, 2026 · 13 min · Justin
The Shape of Decoupling

The Shape of Decoupling

There is a moment in every codebase where someone draws a box on a whiteboard, labels it EventBus, and draws arrows fanning out to every other box. The room nods. It looks like architecture. It looks like the right architecture — loosely coupled, extensible, the textbook answer to “how do we let many components react to one thing happening?” That diagram is a trap. I drew it for Moments, my photo manager, in late March 2026. I had the design reviewed by an external UI architect. I shipped it in April across six phased PRs. And on the third of May I deleted the whole thing in a single commit titled, with some satisfaction: ...

May 10, 2026 · 8 min · Justin