Debuggability by Design

A system that can answer what happened, why, for whom, on which version. Stable ids, deterministic cores, and time and randomness as injected dependencies.

Debuggability by Design

A system should be able to answer what happened, why, for which request and user, on which version, and from which state. None of those are answerable later if the design did not record them.

Q · Six weeks from now, someone asks why this customer was charged on the 3rd. What has to have been designed in for that to be a ten-minute answer rather than a day?
Logging at Boundaries

Log state transitions and external interactions. A log line is an interface with a future reader, and most debug logging is a message the author sent to themselves an hour ago.

Q · Which events deserve a log line, and which ones cost money, hide the useful lines, and tell a future reader nothing?
Stable Identifiers

request_id, order_id, workflow_id. Correlation is a design decision made in the first week or not at all, because an id cannot be added to records that were written without it.

Q · A customer says "it did not work". What single value do they, or support, hand me that finds every record of what happened?
A Deterministic Core

Same inputs, same outputs, every time. A domain core with no ambient time, randomness, I/O or global state can be tested exhaustively, replayed from a log, and reasoned about without running it.

Q · Why can I not reproduce this bug locally, and what would the code have to look like for the answer to be "paste the inputs and run it"?
Time as a Dependency

A `now()` buried in a rule makes the rule untestable and unreproducible. Injecting a clock fixes that and costs a parameter threaded through code that did not want one — which is a real price, not a rounding error.

Q · Which parts of this system need time to be an input rather than something the code reaches for, and where is threading a clock through not worth it?
Randomness as a Dependency

The same argument as the clock, applied to anything that returns a different answer each call: id generation, shuffling, sampling, jitter. Injected, they are reproducible; ambient, they are a bug you cannot re-run.

Q · Which of the non-deterministic values in this code are decisions I will need to reproduce, and which are noise that should stay ambient?