Skip to main content

Not rules. Not dogma.

How I think about building software

Principles I've found consistently worth defending. These are things where I have a real point of view, not table stakes that every senior engineer knows.

PACELC over CAP

The CAP theorem is the wrong tool for most design conversations. PACELC (which also covers the latency/consistency tradeoff during normal operation, not just during partitions) maps more directly to the decisions I actually make.

When I'm designing a system, I ask "what happens when there's no partition?" because that's 99.9% of the time, and the latency/consistency tradeoff during normal operation usually matters more than the partition behavior. Black Skies uses Redis for hot state (PA/EL), etcd for coordination (PC/EC), and DynamoDB for durable global state (PC/EC with coalescing). The CAP labels don't capture those distinctions.

Architecture decisions are the product

Code is replaceable. Architecture decisions are expensive to reverse. I use ADRs not as documentation theater but as a forcing function: if I can't articulate why I'm choosing approach A over approach B in writing, I haven't thought about it enough.

The decision record outlives the code. The Black Skies codebase will change significantly as I learn from load testing, but the ADRs explaining why H3 over quadtree, why worker-per-region over single-server, why three-tier storage — those should stay valid. They're the accumulated reasoning that makes future decisions faster and more consistent.

Test the boundaries, not the implementation

The testing model I use: unit tests verify contracts and edge cases, not implementation details. Integration tests verify that components compose correctly at boundaries. E2E tests verify user-visible behavior.

When AI tools generate code, the tests should catch architectural violations, not cosmetic differences from what a human would have written. A test that fails because a variable was renamed is a bad test. A test that fails because a component now bypasses the circuit breaker is a good test.

Observability is a design constraint, not a feature you add later

If I can't answer "what's happening in this system right now?" from the telemetry alone, the system isn't done. Structured logging, distributed tracing, and metrics are designed into the architecture from day one. They're as load-bearing as the business logic.

This is why Black Skies has a tracing span for every boundary crossing, a metric for every fencing token rejection, and a structured log event for every state transition. The debuggability of the system under load is part of its correctness.

AI tools are collaborators with guardrails, not autonomous agents

I use AI coding tools extensively. I also don't trust them. The value of AI-assisted development is speed within controlled boundaries. The risk is drift: code that passes tests while violating architectural invariants the AI didn't know about.

My workflow gives AI tools explicit architectural context (via CLAUDE.md, ADRs, spec files) and verifies output against those constraints. This is what "spec-driven development" means in practice. The AI doesn't get to make architectural decisions. It gets to implement within the boundaries I've set.

Prefer boring technology until the problem forces your hand

Default to well-understood, battle-tested tools (Postgres, Redis, Go, Kubernetes). Reach for novel technology only when the problem specifically demands it and the mainstream option has a documented limitation that matters for your use case.

H3 hexagonal indexing for Black Skies is an example of reaching for a specialized tool because rectangular grids have a genuine, documented problem (non-uniform adjacency) that matters for the game's spatial model. Most of the time, Postgres is the answer.

These principles are a snapshot of how I think in mid-2026. They'll evolve as I learn from the Black Skies load tests and whatever comes next. The only constant is that I'll keep writing them down.