smallbox

Decisions

Why these systems are shaped this way — and what it cost.

The architecture map shows the shape of the systems. This page shows why that shape exists — the decisions that were genuinely hard, what each one bought, and what each one is still paying for. Two live systems: a financial-literacy platform and an equity-research product. And further down, the part that matters most day to day — what you would actually be able to see of anything I build for you.

Decision one

The dependency already existed. I made it visible to the compiler.

One product, many independently running services, talking over HTTP. The backend depends on every subsystem’s field names whether or not anything declares it — rename a field in the email service and the backend has to adapt either way. The dependency was never optional. Only its visibility was.

The tension
A hidden dependency is comfortable right up until it fails, and it fails at runtime, in front of someone. Making it explicit means accepting a coupling that looks worse on a diagram.
The alternatives
Published, versioned packages with compatibility windows — the full contract ceremony. Or raw HTTP with hand-written types on both sides: nothing to maintain, and a renamed field fails in production instead of in the build.
The decision
Each subsystem ships a typed client that the backend references from source. HTTP lives only inside that client, called only from one layer. References across a boundary are ids, never shared foreign keys.
The mechanism
The compiler. A changed contract stops the build — no discipline, no review step, no remembering required.
What it purchased
Stated exactly, because the precision is the point: not that drift becomes impossible, but that drift surfaces at compile time instead of runtime. The ceremony option buys independent contract evolution, which is worth nothing with one product and one release cadence.
What it cost
Coordinated builds, and a source reference that has to be present wherever the backend compiles. Those are costs of referencing across repositories and building during deploy — not of typed contracts themselves. Packages, a single repository, or built artifacts would each keep the compile-time safety without them; each also reintroduces something this decision was avoiding.
Where it is weaker
One seam has no such protection, and it behaves exactly as the theory predicts: two copied enumerations bridged by their numeric values, so reordering one silently reclassifies data — no compile error, no exception. A hand-written test stands in for the missing compile error, and it is strictly weaker than the thing it replaces. And once, a stale build artifact bypassed the guarantee entirely: a new field simply absent at runtime, no build error. The honest limit that revealed — the compiler protects only the source graph it actually recompiles — is better than the original claim.
Reconsider when
External consumers appear, or separate teams ship on independent cadences. Then the same seam becomes a versioned package. The boundary doesn't move; only how it's distributed.
ONE DEPENDENCY, TWO CONNECTIONSthe backendcomposes the productits typed clientshipped by the serviceBUILD TIMEa renamed field stops the buildthe contract, and no datathe serviceowns its own dataRUN TIMEthe call over HTTP, and the dataAND THE ONE SEAM WITHOUT ITthe backenda copied enumNo build-time line here. The two copies are bridgedby number, so a reorder reclassifies data silently.
Two connections, one dependency. The build-time line is not a second pipe for data — it carries nothing at run time. It exists so the compiler knows what the HTTP call below it is promising, which is why a renamed field becomes a failed build instead of a broken page. Underneath, the one seam with no such line: two copies of the same enumeration bridged by their numeric values, where a reorder reclassifies data with no error at all, and a hand-written test stands in for the compile error that isn’t there.

Three decisions sit close together here and none of them requires the others: typed contract visibility makes contract changes stop the build; star-shaped composition means one composition point instead of a subsystem-to-subsystem web; independent runtime and data ownership lets each service keep its own deployment, backup and recovery policy. They reinforce each other. Treating them as one decision would make the argument tidier than it is.

Decision two

The thing that makes a claim cannot be the thing that certifies it.

The research product turns company figures into small structural claims. The temptation is to have the code that produces a claim also confirm it — it already has the numbers loaded, and it would be much less work.

The tension
A system that grades its own homework will pass. Checking a claim through the same code that produced it proves the code agrees with itself, which is not the same as the claim being right.
The decision
A claim is re-derived by a separate verifier working from the stored source figures, independent exactly where shared implementation could reproduce the same defect. That reduces correlated implementation errors — a premise both were handed can still fool both — and it is what makes a recomputation a proof rather than a re-run.
The mechanism
Each check writes a durable record — the claim, the inputs it consumed, the step-by-step calculation, and the date it was true as of. The receipt is kept, not just the verdict, so trust can be re-derived later instead of remembered.
What it purchased
A real defect, caught by the mechanism working as designed. A square-root routine silently failed to converge on large inputs, inflating a value roughly 4.75× and firing false maximums. The independent path, using different arithmetic, produced 215 contradictions — and the count named the bug. After the fix: 215 → 0, and the measure’s trust level rose, because the check had improved. The original path carried its own faulty assumption, so it could not expose the error.
What it cost
Two implementations of the same arithmetic, deliberately kept apart, and a standing rule against the obvious refactor that would merge them. The duplication is the feature; that has to be written down or someone tidies it away.
Where it is weaker
The strongest level is called reproducible, not true, and the ceiling is written into the product: reproducible from the reported figures, assuming those figures are accurate. Recomputation confirms consistency with what a company reported. It cannot confirm the company. A claim can also be perfectly reproducible and still be the wrong thing to show — so whether a number can be trusted and whether it should be published are answered by different code, on purpose.
Reconsider when
A second implementation in another language would be the stronger form — cross-implementation agreement on shared fixtures turns reproducibility into something anyone can watch run. That is the direction, not the current state.

Both surfaces are public: the live trust ledger, which publishes each measure’s verification state including the untrusted ones, and the dated public correction record.

Decision three

Different data deserves different care.

The two systems disagree about schema change, on purpose. This is the pair I would offer if someone wanted to know whether the decisions are reasoned or inherited.

The tension
One rule for handling schema change across everything is simpler to remember, and wrong. The right level of ceremony depends on what happens if the data is lost.
The decision
In the learning platform, migration files are blocked at the commit hook: schema regenerates, and on a change you drop the database and reseed. In the research product, a schema change without a migration is a stop-and-ask rule, and production applies migrations on startup.
The mechanism
A commit hook that refuses the wrong artifact on one side; a release gate and a service that won't start on the other. Both mechanical. Neither depends on remembering which system you are in.
What it purchased
The platform’s data is regenerable simulation output — reproducible in one command from a fixed seed — so migration ceremony would be pure cost. The research product holds a month of paid vendor pulls, paid model output and real user state, which cannot be rebuilt at any price. The decision was never about the database tooling. It was about whether the data can be recreated.
What it cost
On the regenerate side: seven hand-written conditional column additions carried in the test fixture, because regenerate-on-start never alters an existing table. On the migration side: four scars, including a 93-minute outage caused by an unguarded foreign-key drop against leftover renamed objects.
Where it is weaker
The regenerate rule exports a manual step to production every time a column is added, and that step is easy to forget precisely because nothing enforces it. The written cost is real and currently unpaid.
Reconsider when
The simulation output stops being disposable — the moment a real player's history matters, that system inherits the other system's rule, and the commit hook has to be removed deliberately rather than worked around.

Whatever gets built, you can see it working.

A system you cannot inspect is a system you have to take on trust. So each of these systems has surfaces whose only job is to explain the system back to the person it was built for — what it is, what it just did, and how it is doing right now. None of these is a report written about the software. They are screens inside the running software.

A generated map of how a system works, with a generation timestamp
The system map, generated from the database on every visit — so it cannot go out of date.
A guide listing every rule with its effect and reach
Every rule in the product, in one readable list, with what each one actually does.
A page describing how property value changes are recorded, with the ledger that stores every change
Every value change written down as its own row, so “why did that number move?” is a question with an answer.
A dashboard showing per-service data freshness and coverage
Live data health: what is fresh, what is stale, what was never fetched. Including the gaps.

That is the loop I care about most: you ask for something, it gets built, and then the thing that was built can show you what it is doing without me standing next to it explaining. If a system can only be understood by the person who wrote it, it is not finished.

How strongly is each boundary actually held?

A boundary is only as real as the thing that enforces it, and that ranking predicts where the bugs will be.

  1. CompilerCross-service contracts. Break one and the build fails.
  2. Commit hookMigration files blocked · data access in the wrong layer blocked · interface changes without a client re-sync blocked · the business test suite on every commit.
  3. Release gateA schema change ships a migration or the service refuses to start.
  4. TestThe deterministic core; the ledger invariants.
  5. ConventionReal in the design, held nowhere in code.

Both systems’ most interesting failures lived on the bottom rung.

A boundary existed between a company’s own reported figures and the system’s generated reading of that company. Real in the design, held only by convention where a report gets written — so generated readings were attributed with “the company describes itself as…”. On well-known companies nobody notices, because the guess is usually right. On an obscure one the model invented a claim and the system printed it as that company’s own words.

The problem was never that a model generated prose; it was that the system labelled model-prose as company-prose. The fix was one change, exactly at the boundary — which is the tell of a real boundary fix. It is tiny, because the boundary was already the right one. It just was not being held.

How the AI collaboration is run.

I use AI heavily while building, and stating that plainly is part of the evidence rather than a footnote to it. The useful thing to show is the structure that keeps it from becoming the architect.

The instructions it works under are split by how fast each part goes stale: a small set of hard limits that never change; durable rules that change only as deliberate redesign; and a fenced, dated section holding what is true today, explicitly labelled as the part that rots — with a standing rule that when it disagrees with the code, the code wins. A fast-rotting fact is structurally prevented from corrupting a slow-rotting rule.

Beside it sits a correction log: 83 recorded corrections, each one a moment where a judgement was wrong and the reason it was wrong. A correction enters once and persists. That is the same feedback shape the products use — output meets reality, the signal comes back, the system adjusts.

AI is reliable at “does this work” and unreliable at “should this exist here” — and the second question is the one that compounds.

The clearest example is one where it was wrong and I overruled it. It proposed keeping a local mirror of another service’s data in sync rather than reading from the owner: measured, written up, shipped, working. I found the backend holding a full copy of tables it did not own, and called it as a boundary violation. Two of the supporting arguments collapsed on inspection — “network calls are expensive” turned out to be under one percent of the job, and had never actually been calculated. The copy came out. Working code that is architecturally wrong is harder to catch than broken code, and no test will tell you.

What I’d want you to know that doesn’t flatter me.

Every claim above is checkable, so the gaps should be too.

  • A deploy fix is written and not applied. One push arrived as twenty-two separate delivery attempts; concurrent installs shredded each other and took production down for about 36 minutes. The fix — take a lock, then do nothing if that commit is already deployed — is ready and has not shipped.
  • Named debt still bit me. One duplicated table was registered, visible and scheduled for cleanup the entire time, and still produced two production defects: divergent figures for the same company, and 13,732 companies rendered wearing another company’s logo. Naming debt is not the same as collecting it.
  • Two decision records still say “awaiting approval” while the code shipped. Good at recording decisions and their reasoning; weak at recording when one actually landed.
  • The map’s own honesty check has not been run since May. There is a routine for re-testing whether the written guidance still matches the code. It exists. It has not been executed.

A first week, then we both decide.

€800 for the first week. I investigate the system you are most nervous about and hand back something written and dated: a defect register with an owner for each item, a map of which boundaries are actually enforced, and a ranked list of what to fix first — including what I could not verify. Not a refactor, and useful even if we stop there.

If it is worth continuing, ongoing ownership runs at around €3,000 a month — the arrangement where someone holds the system rather than visiting it.

Consistent with the rest of this page: it was assembled from the repositories and the correction log with AI assistance. The decisions are mine; the assembly was not. Saying so costs nothing, and it is the same provenance rule the systems above are built on.