making-invalid-states-unrepresentable
npx machina-cli add skill msewell/agent-stuff/making-invalid-states-unrepresentable --openclawMaking Invalid States Unrepresentable
Workflow A: Review Existing Code
- Identify the scope (module, file, type, or function under review).
- Read the code and enumerate the types, fields, and state representations.
- Detect anti-patterns (see checklist below).
- For each finding, propose a concrete refactoring using the appropriate technique from
references/. - Assess tradeoffs — see
references/advanced-topics.md§ Tradeoffs. - Present findings as a prioritized list: high-value (core domain, security, APIs) first.
Workflow B: Design New Types
- Collect domain requirements: valid states, invalid states, transitions.
- Choose modeling techniques — read
references/core-and-parse.mdfor foundational approach. - Model with types: sum types for alternatives, product types for combinations, phantom/branded types for compile-time tags.
- Push validation to boundaries: parse external input immediately into precise types.
- Validate the design: confirm invalid states cannot be constructed.
- Iterate as domain understanding deepens.
Anti-Pattern Detection Checklist
Flag code that exhibits any of these — see references/anti-patterns-and-examples.md for details and fixes:
- Primitive obsession — raw strings/ints used for domain concepts (IDs, emails, money)
- Boolean blindness — booleans that lose context about what was verified
- Optional property proliferation — many optional fields instead of a union type
- Stringly-typed code — open strings where a closed set of literals applies
- Shotgun parsing — same validation repeated in multiple call sites
- Boolean soup — multiple booleans encoding what should be a state machine
- Unguarded constructors — types with invariants that can be constructed without validation
Technique Selection Guide
| Situation | Technique | Reference |
|---|---|---|
| Mutually exclusive states | Sum types / discriminated unions | references/techniques-structural.md § 1 |
| Domain-constrained primitives | Newtype + smart constructor | references/techniques-structural.md § 2 |
| State machine / workflow | Typestate pattern | references/techniques-structural.md § 3 |
| Collection must be non-empty | NonEmpty collection | references/techniques-structural.md § 4 |
| Prevent argument swaps | Branded / opaque types | references/techniques-type-level.md § 5 |
| Compile-time state tagging | Phantom types | references/techniques-type-level.md § 6 |
| Required fields enforcement | Type-safe builder | references/techniques-type-level.md § 7 |
| Complex numeric/range invariants | Refinement types | references/techniques-type-level.md § 8 |
Core Principle (Summary)
Design types so invalid states cannot be constructed. Let the compiler enforce correctness. Parse at boundaries, trust types internally. See references/core-and-parse.md.
Output Format
When reviewing code, present each finding as:
- Location — file, line, type/function
- Anti-pattern — which pattern from the checklist
- Risk — what invalid state is possible
- Recommendation — specific technique and refactored type signature
- Priority — high (core domain/security/API), medium, low
When designing types, present:
- Domain states — enumerated valid and invalid states
- Type definitions — concrete code in the user's language
- Boundary parsers — constructors/factories that validate at entry points
- Tradeoff notes — where pragmatism overrides purity
References
references/core-and-parse.md— Core principle and parse-don't-validate patternreferences/techniques-structural.md— Sum types, newtypes, typestate, NonEmptyreferences/techniques-type-level.md— Branded types, phantom types, builders, refinement typesreferences/anti-patterns-and-examples.md— Anti-patterns to detect and real-world examplesreferences/implementation-guide.md— Step-by-step process, code review checklist, language support, related principlesreferences/advanced-topics.md— Advanced techniques, modern applications, industry trends, tradeoffs, resources
Source
git clone https://github.com/msewell/agent-stuff/blob/main/skills/making-invalid-states-unrepresentable/SKILL.mdView on GitHub Overview
This skill guides analyzing code to design type systems that prevent invalid states from being constructible. It centers on techniques like sum types, newtypes, typestate, branded types, and parse-don't-validate, and supports both reviewing code for bugs and designing new domain models.
How This Skill Works
You inspect the codebase to enumerate types and invariants, choose modeling techniques from the reference material, and push validation to boundaries by parsing external input into precise types. The process emphasizes ensuring invalid states cannot be constructed and iterating as domain understanding deepens.
When to Use It
- Reviewing code for invalid-state bugs and anti-patterns
- Refactoring types to eliminate impossible states
- Designing domain models with strict invariants
- Applying compile-time correctness patterns in new or existing code
- Language-agnostic guidance to enforce invariants at the type level
Quick Start
- Step 1: Identify scope and enumerate domain states and invariants
- Step 2: Choose modeling techniques (sum types, newtypes, phantom/branding, typestate) and sketch type definitions
- Step 3: Implement boundary parsers and a type-safe builder to enforce invariants at entry points
Best Practices
- Flag primitive obsession and boolean blindness; prefer closed sets via unions
- Use sum types over multiple booleans or scattered flags
- Push validation to boundary parsers and use parse-at-entry
- Encode state with branded/opaque types and phantom types to prevent mixing
- Adopt type-safe builders or smart constructors to enforce invariants
Example Use Cases
- Replace a status field with a discriminated union of Status variants (Pending | Approved | Rejected)
- Apply typestate to enforce a multi-step workflow (Draft -> Review -> Publish)
- Wrap IDs and monetary values in newtypes to prevent domain confusion
- Use phantom types to distinguish compile-time states in a protocol
- Parse external input at the boundary into precise types instead of validating later
Frequently Asked Questions
Related Skills
creating-c4-diagrams
msewell/agent-stuff
Creates, reviews, and interprets C4 software architecture diagrams (System Context, Container, Component, Dynamic, Deployment). Produces Structurizr DSL or Mermaid diagram code following C4 model best practices. Use when creating architecture diagrams for a system, reviewing existing C4 diagrams for correctness and anti-patterns, generating Structurizr DSL workspaces, producing Mermaid C4 diagrams for READMEs, or using C4 diagrams as context for design decisions, code generation, risk analysis, or onboarding.
arazzo-specification
msewell/agent-stuff
Guides writing, reviewing, and modifying Arazzo workflow specifications (OpenAPI Initiative standard for multi-step API workflows). Use when creating Arazzo documents from scratch, adding steps or workflows to existing specs, reviewing Arazzo files for correctness, or generating API workflow definitions. Covers document structure, runtime expressions, success criteria, control flow, data threading, reusable components, workflow composition, AI agent integration, and validation.
kotlin-functional-programming
msewell/agent-stuff
Guides writing idiomatic, functional-style Kotlin code using built-in language features. Use when asked to write, review, or refactor Kotlin code for immutability, pure functions, sealed types, error handling, collections, coroutines, or functional architecture patterns.
property-based-testing-with-kotest
msewell/agent-stuff
Writes property-based tests using Kotest's kotest-property module. Identifies testable properties, designs generators, and configures PBT for Kotlin/JVM projects. Use when writing property-based tests, creating custom Arb generators, choosing property patterns (roundtrip, invariant, idempotence, oracle), debugging shrunk counterexamples, or integrating PBT into a Kotlin test suite alongside example-based tests.
reducing-coupling
msewell/agent-stuff
Analyzes a codebase scope for coupling issues, diagnoses coupling types using the Connascence framework, and proposes a comprehensive refactoring plan with concrete code changes. Use when asked to find coupling, reduce dependencies, decouple modules, or improve modularity in a codebase.
mermaid-sequence-diagrams
msewell/agent-stuff
Generates, reviews, and fixes Mermaid sequence diagrams following syntax rules and best practices. Use when creating sequence diagrams from system descriptions, reviewing existing Mermaid sequence diagrams for correctness, fixing parse errors, or refactoring large diagrams into focused sub-diagrams. Covers participants, arrows, activations, control flow, notes, styling, and common anti-patterns.