Skip to main content
skill CX Codex

design · rainmanjam — Claude Code skill: types that make misuse unwritable

This skill shapes APIs, schemas and state machines so a wrong call cannot be typed at all. Use it while a new interface is still being drawn up.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add rainmanjam/poka-yoke --skill design -a claude-code
Open on GitHub

What design does

Before you fix a signature, the skill pushes you to list how the thing will be abused: two same-type arguments sitting side by side, a step the caller has to remember, a struct with optional fields that yields nonsense combinations, an empty filter handed to a delete call. Those answers decide the shape of the API.

Then come the devices, strongest first. Give each concept its own type instead of a bare string. Swap a bag of optionals for a discriminated union. Parse at the edge into a new type rather than validating and returning a boolean. Encode order in typestate, and make an exhaustive switch break the build when a variant appears. When the language has no way to say it, the constraint drops into the database as NOT NULL, CHECK, UNIQUE or a foreign key.

Error swallowing is called out directly. An empty catch block or a default value on a failure path turns a loud defect into a quiet one. Destructive calls should refuse to run by default, and an idempotency key belongs in the required argument list, not the optional one. On the bonus side, the skill wants the reasoning written down next to the code: what you ruled out, what stayed possible and why. Interfaces that already exist are handled by audit instead.

What it helps with

  • design an API where adjacent same-type arguments cannot be swapped
  • replace a struct full of optional fields with a discriminated union
  • move input checks into a parser at the system boundary
  • block out-of-order method calls with a typestate machine
  • turn a missing enum case into a compile error

Example prompts for the agent

  • Design the order type so an invalid state has no spelling
  • Write the signature for transfer(from, to, amount) and show how to stop callers mixing up the arguments
  • Sketch a state machine for a claim where commit() does not exist before validation

How to install design

1Skills CLI (Vercel)

npx skills add rainmanjam/poka-yoke --skill design -a claude-code

2Manually: put the files in place

~/.claude/skills/design/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/design/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about design

How do I install design in Claude Code?

Run npx skills add rainmanjam/poka-yoke --skill design -a claude-code. The skill lands at ~/.claude/skills/design/SKILL.md.

What does design do?

It guides the layout of types, schemas and state machines so an invalid call fails to compile. Expect to use it at the design stage of a new interface.

How is design different from audit?

design targets code that does not exist yet. Once an interface is written, the companion skill audit takes over.

Source text

SKILL.md · MIT
---
name: design
description: >-
  Design APIs, schemas, types and state machines so misuse cannot be expressed. Use when writing a new interface and someone asks "what should the types look like", "make invalid states unrepresentable", "so callers cannot screw it up", or wants illegal state transitions rejected. Covers branded types, discriminated unions, typestate, parse-don't-validate. For code that already exists use audit.
---

# Poka-Yoke Design

Mistake-proofing is cheapest before the code exists. Once an interface has callers, every
device you add is a migration; before it has callers, a device is free. So the work here is
front-loaded: decide how this thing will be misused, *then* pick the shape that makes the
misuse unsayable.

This is Shingo's **source inspection**: checking the conditions that produce errors rather
than the errors themselves, and it is the strongest of his three inspection types, because
the error never gets the chance to happen.

## The ritual: enumerate misuse before you write the signature

Before writing an interface, spend real effort on this list. It takes two minutes and it
determines the design.

1. **What are the parameters, and can any two be swapped without complaint?** Same type
   adjacent to same type is among the most common footguns in software.
2. **What must a caller remember to do?** Call something first. Call something after. Check a
   return value. Close a handle. Pass the right units. Every "must remember" is a defect
   scheduled for later.
3. **What states can this thing be in, and which combinations are nonsense?** If you can
   construct a value that means nothing, the type is wrong.
4. **What happens on the second call?** Retries, double-clicks, at-least-once queues. If the
   answer is "it charges twice," you need a motion-step device.
5. **What's the worst plausible input?** Empty set, enormous set, null, wrong tenant,
   yesterday's token, a string from an attacker.
6. **When someone adds a new case next year, what breaks?** The right answer is "the build."
   The wrong answer is "nothing, it silently falls through."

Write the answers down where the user can see them, briefly. Then design against them.

## The moves, in preference order

Reach for the highest one the language and situation allow. Each rung down is a real
concession, take it consciously and say why.

### 1. Make the illegal state unrepresentable (Control, contact lens)

The strongest move: change the type so the bad value has no spelling.

- **Distinct types for distinct concepts.** `UserId` and `OrderId` are not both `string`.
  Money is not a float. A timeout is not a bare number. Branded types / newtypes / value
  objects cost almost nothing and kill an entire class of swap-and-mix-up bugs.
- **Sum types over bags of optionals.** `{status, error?, data?, retryAt?}` permits states
  like "succeeded with an error and a retry time." A discriminated union permits exactly the
  states that exist. If your struct has N optional fields, it claims 2^N states are legal;
  ask how many actually are.
- **Non-empty and bounded collections** when zero or unbounded is nonsense.

### 2. Parse, don't validate (Control at the boundary)

Validation returns a boolean and throws the knowledge away; parsing returns a *new type* that
carries the proof. `validateEmail(s: string): boolean` leaves every downstream function still
holding an unvalidated string. `parseEmail(s: string): Email | Error` means downstream
functions that take `Email` cannot receive garbage: the type system carries the guarantee
for you, forever, for free.

Do this once, at the system's edge: HTTP handlers, queue consumers, config loading, file
parsing, and every third-party response. Inside the boundary, work only with parsed types.

### 3. Make order and lifecycle enforceable (Control, motion-step lens)

When steps must happen in sequence, encode the sequence in types rather than in prose:

- **Typestate**: each operation consumes one state and returns the next, so `.commit()` does
  not exist on an uncommitted-and-unvalidated value.
- **Builders that cannot `build()`** until required steps have run, enforced by the type,
  not by a runtime check, where the language allows it.
- **Constructors that return ready objects.** If `init()` must be called before use, the
  constructor is doing the wrong job. Give it a static factory that does both.
- **Scope-bound resources**: context managers, `defer`, RAII, `using`. Never "remember to close."
- **Idempotency keys as required parameters** for anything that moves money, sends a message,
  or mutates external state. Required, not optional: an optional idempotency key is a
  suggestion, and suggestions are rung zero.

### 4. Make completeness checkable (Control/Warning, fixed-value lens)

- **Exhaustive matching** with a compiler-enforced never/unreachable arm, so adding an enum
  variant breaks the build at every site that must change. This is one of the highest
  leverage devices in existence and it costs one line per switch.
- **Required arguments over defaulted ones** when there is no safe default. A default that is
  wrong half the time is worse than no default: it hides the decision.
- **Whole-config validation at startup**, so a missing variable fails the deploy rather than
  the 3am request.

### 5. Fail fast and loud (Warning)

When the type system genuinely cannot express the constraint, assert at the boundary and
throw. This is a real poka-yoke, one rung down. Make the message name the mistake and the fix.

Two rules that decide whether this rung works at all:

- **No silent fallbacks.** `catch {}`, `except: pass`, `|| defaultValue`, `unwrap_or_default()`
  on an error path. These are devices *removed*. They convert a loud mistake into a quiet
  one, which is exactly backwards. If a fallback is genuinely correct, the comment must say
  which failure it is absorbing and why that failure is expected.
- **Destructive operations default to safe.** Dry-run by default, require an explicit
  predicate, refuse to act on an empty or oversized set. `deleteUsers(filter)` with an empty
  filter should raise, not truncate the table.

### 6. Where the language can't help, move the device to the data layer

The database is a type system that all your services share. `NOT NULL`, `CHECK`, `UNIQUE`,
foreign keys, and partial unique indexes are Control-rung devices that hold even when someone
writes a script, connects with `psql`, or ships a service in another language. When
application-level enforcement is the only thing standing between you and corrupt data, push
it down.

## Deliver the design with its reasoning attached

You were asked for code, so write the code. But narrate the mistake-proofing in a few lines,
because the reasoning is what stops it being undone later:

- what misuses you enumerated,
- which ones the design now makes impossible, and at which rung,
- which ones you consciously left possible, and why.

That last bullet matters most. Every design leaves something possible; naming it is the
difference between a considered tradeoff and an oversight.

## Restraint

Mistake-proofing has a cost, and past a point it stops paying. Signs you have gone too far:
five wrapper types for one function, a builder for a two-field struct, a type parameter no
caller will ever understand. The test is whether the device prevents a mistake someone would
*plausibly make*, weighted by what happens when they do. An internal helper with two callers
and a trivial failure mode does not need a newtype; a public payments API does.

Sean Goedecke's [critique of the maximalist version](https://www.seangoedecke.com/invalid-states/)
is worth taking seriously: types that model every invariant can become harder to change than
the bugs they prevent. Aim the strongest devices at the highest blast radius, and leave
low-stakes code readable.

Read `../../references/hazard-catalog.md` for the misuse shapes worth
enumerating, and the matching `references/lang-*.md` for what your language can actually
express: the moves above are only as strong as the type system underneath them.

© rainmanjam · MIT · rainmanjam/poka-yoke

Similar skills

All skills for Backend and APIs →

design by other authors comes first.

design claudekit Generates logos, brand identity assets, design tokens, Chart.js HTML slides, and multi-platform banners using Python scripts and AI providers. CX Codex +1 41,800installs design SubframeApp Guides an agent through Subframe work: build a page or component, line up design options on a canvas, adjust a theme. CX Codex +1 775installs design rshankras Design skills for Apple platform UI: Liquid Glass, animations, game feel, UX writing, SF Symbols and typography. Useful when a screen feels flat and you need a plan. CX Codex +1 677installs design cognitedata Provides Aura design system rules for Flows and Fusion applications. It guides agents to choose primitives, rely on semantic tokens, and maintain consistent UI states. CX Codex +1 1,900installs design kirkchen This skill prepares Beat change specifications with proposal, gherkin, and design.md artifacts. Use it when a change is being started. CX Codex +1 506installs api-design margelo This skill shapes and reviews public APIs for TypeScript, JavaScript, React, and React Native libraries before implementation. CX Codex +1 609installs public-api-design-review Samuel Berthe Walks a REST API surface through a checklist: resource naming, methods, status codes, pagination, error shape. Findings land in Must-change or Improvement. CX Codex +1 7,800installs principle-type-system-discipline backnotprop Enforces strict type system rules: brand semantic primitives, parse external data at boundaries, and derive types directly from schemas. CX Codex +1 5,300installs code-craft jssblck code-craft guides type boundaries, invariants, state, effects, errors, and abstraction choices. Use it when an agent is shaping a code change. CX Codex +1 1,700installs system-design official anthropics Walks you through designing services and systems: requirements, components, trade-offs. Useful when you need to decide how to split a service into parts. CX Codex +1 7,500installs rest-api-design aj-geddes This skill gives your agent rules for resource naming, HTTP methods, status codes and versioning when you build new endpoints or clean up old ones. CX Codex +1 2,100installs

From the same set7

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor