From grimoire
Asserts internal assumptions (preconditions, postconditions, invariants) in functions, modules, or services to crash loudly at the violation site rather than propagating corrupt state downstream.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-fail-fastThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Assert internal assumptions; crash loudly at the violation site, not downstream.
Assert internal assumptions; crash loudly at the violation site, not downstream.
Adopted by: Google Engineering Practices (assert liberally, crash on invariant
violation), Amazon SRE (loud failures over silent degradation), Hunt & Thomas
"Pragmatic Programmer" ("dead programs tell no lies"), Erlang OTP ("let it crash"
philosophy), and all major languages' standard library design (Java Objects.requireNonNull,
Python assert, Go panic, Rust unwrap/expect).
Impact: Microsoft Research found that 70% of production bugs in distributed systems
originate from error propagation, not the original fault (Nanz et al., 2015). Terminating
at the violation site reduces mean diagnosis time from hours to minutes because the stack
trace points directly at the violated assumption.
Why best: The alternative — defensive recovery ("if null, use default; if wrong type,
coerce") — hides programmer errors and allows corrupt state to propagate until it
manifests as a mysterious failure far from the origin. Fail-fast makes the defect visible
immediately.
Sources: Hunt & Thomas, "Pragmatic Programmer" 2nd ed. §23; Hoare (1969); Fowler, martinfowler.com/ieeeSoftware/failFast.pdf; Erlang OTP Design Principles
| Error type | Source | Correct response |
|---|---|---|
| Programmer error | Violated assumption (null arg, empty list, illegal state) | Fail-fast: assert/panic/throw unchecked exception |
| Expected failure | External input, network, user action | Graceful error handling (return error, throw checked exception) |
Apply fail-fast only to programmer errors. For external failures, use
validate-external-input.
Check every assumption the function makes about its arguments before using them.
def process_order(order_id: int, items: list[Item]) -> Receipt:
assert order_id > 0, f"order_id must be positive, got {order_id}"
assert len(items) > 0, "items must not be empty"
assert all(i.quantity > 0 for i in items), "all item quantities must be positive"
# ... implementation
After every operation that changes state, assert the invariant still holds.
def transfer(self, amount: Decimal, target: Account) -> None:
self.balance -= amount
target.balance += amount
assert self.balance >= 0, f"transfer created negative balance: {self.balance}"
assert amount > 0, f"transfer amount must be positive: {amount}"
Include both the expected condition and the violating value in the assertion message.
# Poor — no context
assert user is not None
# Good — states the assumption and the violating value
assert user is not None, f"expected authenticated user in session {session_id}, got None"
If code catches an assertion/panic at a high level to "recover," it defeats fail-fast. Allow programmer errors to propagate to the top and crash (or restart the unit of work).
# Wrong — hides the bug
try:
process_order(order_id, items)
except AssertionError:
return default_receipt # bug is now silent
# Right — let it surface
process_order(order_id, items) # if it throws, let it propagate
assert can be disabled, use explicit guardsPython's assert is disabled with -O. For critical invariants, use an explicit raise:
if order_id <= 0:
raise ValueError(f"order_id must be positive, got {order_id}")
validate-external-input instead. Failed
external validation is NOT a programmer error; it is an expected condition.Using fail-fast for expected external failures. If the network times out or a user enters a bad value, that is not a programmer error. Panicking on expected conditions makes the system fragile, not robust.
Catching and swallowing assertion failures. A catch-all except Exception: pass
at the top level silently buries programmer errors. Let them crash with a full stack trace.
Asserting without a message. A bare assert x is not None produces a useless
AssertionError in production logs. Always include the assumption and the violating value.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireApplies Code Complete defensive programming: barricade design, assertion vs error-handling, input validation policy, and correctness-vs-robustness strategy for the current code context.
Provides rigid checklists for correctness traps in error handling, floating-point math, concurrency, remote calls, singletons/globals, hot-path data structures, and high-volume logging. Invoke when writing or reviewing such code.
Strategies for handling errors: exceptions, error types, recovery strategies, and error propagation.