Keep Control Flow Linear
No deep nesting. No hidden jumps. Five levels of nesting = 32 possible paths. Tests cover three — the 29th fires in production.
Gerard Holzmann's ten rules for safety-critical code, generalized beyond C, extended for distributed systems, and annotated for the specific ways AI code generation violates them.
From C in embedded flight systems to Go/Rust in backend services — each rule maps to a specific distributed-systems failure mode and a specific AI generation weakness.
No deep nesting. No hidden jumps. Five levels of nesting = 32 possible paths. Tests cover three — the 29th fires in production.
Explicit ceiling in code, not documentation. Unbounded retries cause the most predictable catastrophic outages.
Every resource opened must be closed on every exit path. Don't acquire what you can't account for under partial failure.
Small enough to hold in your head, describable in a sentence without "and." A 300-line handler can't be unit tested at any component.
Preconditions, postconditions, invariants as code — not comments. A violated assertion at the point of violation is worth 10 hours of post-hoc debugging.
Every error handled, logged, or propagated. A bare catch{} is active suppression of diagnostic information. DEBUG-logged and discarded = months of corrupted state.
Data as close to use as possible. Shared mutable state is the primary source of race conditions and test pollution. Parameter passing over globals.
I/O, mutations, network calls obvious at the call site. formatResponse() that writes to an audit log is a correctness trap. Pure transforms in one layer, writes in another.
Four middleware layers with their own error handling — an error in layer 3 is swallowed by layer 2, surfaced as generic 500. Test full stack error composition.
Warnings are future bugs the toolchain has already found. Linters and static analyzers as hard CI gates. Every warning suppressed is a bet on behalf of on-call.
LLMs optimize for happy-path task completion — they are systematically bad at bounded behavior, explicit error handling, and legible control flow.
AI generates deeply nested conditionals because training rewards task completion over structural clarity. Count nesting levels post-generation — more than two deep = refactoring task.
AI retry logic lacks caps, jitter, and dead-letter handling. Ask explicitly: "What is the maximum? What happens at the limit? Is there jitter?"
AI closes connections on success, leaves them open on failure. Trace every return err — confirm cleanup on every path.
AI skips input validation when surrounding code implies data is already clean. That implication is often wrong. Prompt for explicit preconditions.
AI generates empty catch blocks, `_ =` discards, and DEBUG-level continues. Enforce: every error must be WARN+, raised, or explicitly returned with context.
AI projects almost never include linter config. Set up static analysis as scaffolding before AI writes anything — the tooling catches what review misses.
Database failover. Payment service's fraud detection API returns 503 for 90 seconds. The retry wrapper with exponential backoff and no ceiling begins retrying.
By retry 7, delay is 6.4 seconds. 8,000 goroutines in flight waiting on retry timers. Memory: 400MB → 3.2GB. Payment requests queuing.
Pod OOMs. Restarts. Immediately begins processing queued requests. Three instances restart in sequence. Fraud API, now recovered, receives 24,000 requests in 30 seconds — falls over again.
Rule 02: retry cap of 3 with max delay 2s would have failed fast. Rule 06: WARN-level logging would have made the storm visible. Rule 09: transparent middleware hid the missing cap from code review.
Maps to retry storms, cascading timeouts, work amplification. Bounded loops with jitter are the distributed equivalent of loop ceilings.
Connection pool exhaustion, file descriptor limits, goroutine leaks. Finite at service level AND cluster level.
Contract testing, schema validation — Protobuf, JSON Schema, OpenAPI as executable assertions for distributed contracts.
Structured logging, distributed tracing, error classification. Errors crossing service boundaries must carry origin context.
Event publication must appear in the handler, not buried in a service layer. Hidden writes trigger chains of downstream consequences.
Service mesh sidecars, API gateways, load balancers — each adds latency and failure modes. Know the full request path.
AI-generated code has not been tested by someone who cares whether it works. The model has no incident response rotation. Read every line. Trace every error path. Treat AI output like a PR from a capable contractor who has never been on-call.
"Here are the requirements, here are the expected failure modes, here are the edge cases: write tests that cover them first, then implement." This forces the model to reason about failures before writing happy-path code.
"Never commit code you haven't read in full. You are responsible for what ships. The model has no incident response rotation."
— David Lee, leestack.dev
Software that cannot fail — from Apollo to backend services where failure carries unacceptable cost.
Networked services where failure modes include retry storms, cascading timeouts, pool exhaustion.
LLM-produced code — optimizes for happy path, systematically weak on error handling and bounded behavior.
Automated tooling enforcing rules before execution — linters, type checkers, CI analyzers as hard gates.
Cascading failure where unbounded retries amplify a partial outage into a full cluster-wide collapse.
Configure linters as project scaffolding. The AI's first output will have violations — that's the point. Tooling catches what review misses.
State retry caps, jitter, dead-letter behavior, context cancellation upfront. Write failing tests before implementation.
Trace every function that acquires resources. AI closes on success, leaves open on failure — this pattern is nearly universal.
Count nesting levels. More than two deep in a handler = refactoring task. Extract branches to named functions.
Grep for empty catch blocks and `_ =` discards. Every error: WARN+, raised, or explicitly returned with context.
AI buries writes in utility functions. Extract to named, visible calls. Read path and write path must be structurally distinct.
Non-negotiable. AI has no incident rotation. Trace every error path. Verify every bound. If you can't explain what every line does under partial failure, don't ship it.