Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add Ariel-Rodriguez/programming-skills --skill "ps-explicit-state-invariants"
Install specific skill from multi-skill repository
# Description
Ensures state is intentionally designed with explicit invariants. Use when reviewing stateful code, designing data models, or refactoring boolean flag explosions. Prevents impossible states and race conditions.
# SKILL.md
name: ps-explicit-state-invariants
description: Ensures state is intentionally designed with explicit invariants. Use when reviewing stateful code, designing data models, or refactoring boolean flag explosions. Prevents impossible states and race conditions.
severity: BLOCK
Explicit State & Invariants
Principle
State must be intentionally designed and always valid. Every piece of state must obey explicit invariants at all times.
If you can't state the invariant, the state is already broken.
Benefits:
- Reliability: Impossible states become unrepresentable
- Debuggability: Invalid states eliminated at design time
- Maintainability: Invariants encoded in type system, not documentation
When to Use
Use this pattern when:
- Designing state shapes for components or stores
- Reviewing code with multiple boolean flags
- Implementing state machines or workflows
- Designing APIs that manage state
Indicators you need this:
- Boolean flag combinations creating invalid states
- Race conditions from temporarily invalid state
- Defensive checks scattered throughout codebase
- Comments explaining "don't do X when Y is true"
Instructions
Design state intentionally
- Identify valid states
- List all legitimate states your system can be in
- Not combinations of flags, but discrete states
-
Example:
idle,loading,loaded,error(notisLoading && hasError) -
State the invariants
- What must always be true?
- Write them down explicitly
-
Encode them in types when possible
-
Make invalid states unrepresentable
- Use tagged unions/sum types
- Eliminate independent boolean flags
- Group related data together
Preserve invariants during transitions
- Atomic state changes
- Move from one valid state directly to another
- Never leave state temporarily broken
-
Use transactions or single assignments
-
Explicit transition functions
- Named functions for each state change
- Document allowed transitions
-
Validate preconditions
-
Handle temporary transition data
- Model transitions as distinct states
- Include both old and new data if needed
- Clean up transition state when complete
Common Pitfalls
β Avoid:
- Multiple independent boolean flags
- Undocumented invariants (living in human memory)
- Temporarily invalid state during updates
- Optional fields that are only sometimes valid
β Do:
- Use discriminated unions for mutually exclusive states
- Encode invariants in type system
- Document what must always be true
- Make optionality explicit and intentional
Examples
β Good: Explicit states with invariants
TYPE RequestState =
| Idle
| Loading
| Success(data)
| Error(message)
FUNCTION render(state):
MATCH state:
CASE Idle:
RETURN "Click to load"
CASE Loading:
RETURN "Loading..."
CASE Success(data):
RETURN displayData(data)
CASE Error(message):
RETURN showError(message)
// Invariant: Exactly one state at a time
// Cannot be Loading AND have Error
// Data exists only in Success state
State is explicit. Invalid combinations impossible. Compiler enforces invariants.
β Bad: Boolean flags allow invalid states
STRUCTURE ComponentState:
isLoading: boolean
hasError: boolean
errorMessage: string OR null
data: Data OR null
FUNCTION render(state):
IF state.isLoading AND state.hasError:
// This shouldn't be possible - but it is!
RETURN "???"
IF state.data IS NOT null AND state.hasError:
// Have data AND error? Which to show?
RETURN "???"
// Invariants not enforced
// 16 possible combinations (2^4), most invalid
Flags allow impossible states. No invariants enforced. Defensive checks everywhere.
AI Review Checklist
Before accepting code, verify:
- [ ] What must always be true about this state?
- [ ] Where is that invariant enforced?
- [ ] Which transitions are allowed?
- [ ] Can invalid combinations occur?
- [ ] Are invariants documented?
If invariants live only in human memory β REJECT (BLOCK)
Common Questions
Q: What if I need temporary state during a transition?
A: Model the transition itself as a distinct state (e.g., { status: 'updating', oldData, newData }).
Q: How do I handle optional data?
A: Make optionality explicit: { status: 'loaded'; data: T | null } or use separate states.
Q: Can I have multiple simultaneous states?
A: Yes, but compose them clearly: { formState: FormState; networkState: NetworkState }.
Q: What about gradual migrations from bad state?
A: Wrap legacy state with adapters that enforce invariants at boundaries.
Enforcement
- Code review: Challenge any boolean flag combinations
- Type system: Use discriminated unions to eliminate invalid states
- Documentation: Require explicit invariant statements
- Testing: Verify impossible states truly cannot be created
Related Patterns
- Functional Core, Imperative Shell: Pure functions naturally preserve invariants
- Single Direction Data Flow: Unidirectional flow prevents inconsistent state
- Naming as Design: Names should reflect valid states, not implementation flags
References
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.