From grimoire
Detects hidden side effects, implicit state mutation, and inconsistent error handling in functions and modules. Activates when code surprises callers or debugging traces opaque state changes.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-predictable-codeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Write code that does exactly what its name and signature promise — no hidden behavior, no surprises.
Write code that does exactly what its name and signature promise — no hidden behavior, no surprises.
Adopted by: Functional programming communities (Haskell, Clojure, Elm — purity by default); Go (explicit error returns, no exceptions); Rust (explicit Result/Option types, borrow checker eliminates hidden mutation); React (predictable rendering via immutable state). Predictability is a prerequisite for composability and testability — both Google and Netflix engineering blogs cite implicit side effects as a primary source of production incidents. Impact: Unpredictable code fails at the worst possible time — in production, under load, in combinations the author never tested. A 2020 analysis of post-mortems at Stripe and Google found that the majority of hard-to-reproduce bugs involved hidden state mutation or error swallowing. Predictable code's behavior is fully determined by its inputs — this makes it deterministically testable, logically composable, and safe to call in new contexts. Why best: Exceptions, global state, and hidden side effects are locally convenient but globally dangerous. They move complexity from the call site (where it is visible and manageable) to runtime (where it is invisible until it fails). Predictable code makes all complexity explicit at the call site. This trades a small upfront verbosity cost for a large debugging and maintenance savings.
Sources: Dan North, "CUPID—for joyful coding" (2022); Claessen & Hughes, "QuickCheck: A Lightweight Tool for Random Testing" (ICFP, 2000); Google, "Building Secure and Reliable Systems" (O'Reilly, 2020)
Status: Emerging — predictability as a named code property is gaining traction via CUPID (2022); functional programming communities have practiced it for decades under different terminology.
A function with a name that implies reading or computing should not write, delete, or send anything. A function that has side effects should name them.
# Bad — "get" implies read-only; actual side effect is invisible
def get_user(user_id: str) -> User:
user = db.find(user_id)
analytics.track("user_fetched", user_id) # hidden side effect
return user
# Good — effect is named and separated
def find_user(user_id: str) -> User:
return db.find(user_id)
def find_and_track_user(user_id: str) -> User:
user = find_user(user_id)
analytics.track("user_fetched", user_id)
return user
Swallowed exceptions and silent failures are the most dangerous form of unpredictability — callers receive a result that looks valid but is wrong.
Result/Either/Option types when failure is a valid pathexcept: pass or catch (e) {} without logging and re-raising or explicitly handling// Bad — error swallowed; caller receives zero value with no indication
func GetUser(id string) User {
user, err := db.Find(id)
if err != nil {
return User{} // caller cannot distinguish "not found" from "error"
}
return user
}
// Good — caller knows when to handle the error
func GetUser(id string) (User, error) {
return db.Find(id)
}
Functions that depend on global variables or ambient context (current user, current time, environment variables accessed inside a function) behave differently depending on invisible preconditions.
// Bad — result depends on ambient environment variable; changes without caller notice
function getFeatureFlag(name: string): boolean {
return process.env[name] === "true" // ambient dependency
}
// Good — dependency is explicit and injected
function getFeatureFlag(name: string, env: Record<string, string>): boolean {
return env[name] === "true"
}
A function that modifies its arguments violates the caller's assumption that passing a value is safe. Mutating inputs creates invisible coupling between callers and callees.
// Bad — caller's object is modified as a side effect
function applyDiscount(order, percent) {
order.total *= (1 - percent) // mutates caller's object
return order
}
// Good — return new value; caller's object is unchanged
function applyDiscount(order, percent) {
return { ...order, total: order.total * (1 - percent) }
}
If the name no longer matches the behavior, every call site is a lie. Keep names accurate; update them when behavior changes.
| Name says | But does | Fix |
|---|---|---|
isValid() | throws exception on invalid | rename to assertValid() or return bool |
getUser() | creates user if not found | rename to getOrCreateUser() |
save() | sends email after saving | rename to saveAndNotify() or split |
calculate() | also writes to database | split into calculate() + persist() |
Conflating pure and predictable. Predictable code can have side effects — as long as they are named and expected. sendEmail() is predictable because the name tells you exactly what it does. Pure (no side effects) is a stronger constraint; predictable (effects match the name and contract) is the minimum bar.
Over-engineering for purity. Wrapping every side effect in a monad or effect type is the right call in Haskell; it may be overkill in Python or Go. The goal is that callers are not surprised — use the idioms of your language to signal effects clearly (naming, return types, documentation).
Silent retry logic. A function that internally retries network calls is unpredictable to callers who expect fast failure. Either make the retry explicit (return a retryable error) or name the function to indicate it retries (fetchWithRetry).
npx claudepluginhub jeffreytse/grimoire --plugin grimoireApplies clean code principles (single responsibility, small functions, clear naming, complexity limits) to generated or refactored code. Triggered by 'clean code', 'refactor this', etc.
Scores and improves code readability using six disciplines: meaningful names, small functions, clean error handling, comments, formatting, and unit tests. For code review, refactoring, PR feedback, or establishing quality standards.
Applies clean code decisions for writing or reviewing functions, classes, naming, and non-trivial logic (≥3 lines). Promotes simplicity by removal, short reasoning blocks, and domain examples.