How I build
One blueprint, carried forward
One document: ten decisions in priority order, an architecture map, numbered invariants, and a list of what must never happen. It came out of a production platform under real load. Every new project starts from it and records where it diverged.
What carries everywhere
Boundaries and habits I already know hold up, so I don't re-decide them at the start of a new project.
One enforceable place per rule
Cross-cutting concerns go through one route wrapper or guard chain. A new endpoint is protected because the developer did nothing, not because they remembered something.
Deny by default
A route that declares no permission is closed. An unauthenticated peer, in the peer-to-peer case, fails at the cryptographic handshake before a byte is parsed as a command.
Domain logic lives outside the framework
A core package callable from a route, a background job, or a script — testable without booting the app, without a socket, without knowing a UI exists.
A transactional outbox, not inline side effects
The 'this needs to happen' event is written in the same transaction as the state change. A separate dispatcher delivers it and retries. Nothing gets silently dropped because a third-party API had a bad five seconds.
Explicit state machines
The legal transitions are data, checked by one validator. No status change happens by an ad-hoc update scattered across handlers.
One typed client is the only door from UI to API
No screen opens a socket, reads the store, or runs a permission check directly. Everything goes through one place, so a wrong base URL breaks all the apps the same way instead of one at a time.
Dependency direction is one-way, enforced by the compiler where possible
In Rust, a layering violation is a build error. In TypeScript, it's a lint plus a diagram checked against the actual imports, because an unverified diagram just describes an intention.
Nothing is done until it's been watched working
Driven end to end, as the real user role, in the running app, with the evidence written down against a date. Compiling doesn't count. Tests passing doesn't count.
Where projects broke the mould
What keeps reuse honest: every project writes down what it kept, changed, dropped, and added because the blueprint had never hit this problem.
OpenDerja_TN
The database isn't just an asset — it's the asset.
Code can be rewritten in a weekend. Months of community annotation can't. Every schema shortcut destroys information permanently, so the schema decisions get the care the code usually gets.
Ferry
There is no trusted third party, so every rule is enforced by the machine that cares.
The outbox went from a safety net to the core abstraction, because an offline peer is the normal case here. Deny-by-default moved down to the Noise handshake. 'Nothing hard-deletes' got deliberately inverted for burn-after-read secrets. And 'done' got stricter: two real machines on a real network.
Ferry
The schema-drift CI check and the DB backup ritual were dropped.
They protect an asset Ferry doesn't have. That care was redirected to the OS keychain, which holds the thing that actually can't be lost.
The one line
Nothing is done until it's been watched working.
Everything else — the verification ledger, testing as every real role, reading the response body not the status code — is that sentence, applied. The last chapter of the story is about where it came from.