Skip to content
Development
Skill

/api-design

Use when designing or reviewing a public API, exported function signature, module boundary, exported type/interface, or any contract other code depends on

From plugin
97
2512 skills1 hook

Claude Code

$ npx -y skills add oribarilan/97 --skill api-design --agent claude-code

SKILL.md

api-design.SKILL.md
name: api-design
description: Use when designing or reviewing a public API, exported function signature, module boundary, exported type/interface, or any contract other code depends on

API and Interface Design

Overview

The headline rule, from Scott Meyers (97/55), governs everything else: **make interfaces easy to use correctly and hard to use incorrectly.** Every other decision below is a tactic for that rule — encapsulate behavior so callers can't reach past the contract, lean on the type system so wrong calls fail at compile time.

This is a **rigid** skill. Run the decisions in order. If you can't satisfy one, stop and tell the user what's blocking you.

When to invoke

Invoke when you're about to:

  • Add or change a function/method that will be called from another module, package, service, or repo
  • Export a new type, interface, trait, protocol, or class
  • Define a request/response schema, RPC method, or message format
  • Publish a library, SDK, or plugin contract
  • Change an existing public signature (parameter order, optionality, return type, thrown errors)
  • Add or remove a configuration flag, CLI option, or env-var contract that other code reads
  • Decide what to make `public`, `internal`, `final`, `sealed`, or `private`
  • Review an API design, interface, or public contract for usability, correctness, or ergonomics

If you're not sure whether a change is "public," ask: *will any code outside this file depend on the shape of what I'm about to write?* If yes, invoke.

Non-triggers — do NOT invoke for

  • Renaming a private/local function whose only callers live in the same file
  • Adding a comment or docstring to an existing public function without changing its signature
  • Fixing a bug inside an existing function without changing its signature, parameters, return type, or error contract
  • Reformatting, reordering imports, or other no-semantic-change edits
  • Renaming a single local variable inside one function
  • Adding an internal helper called only from one place that already exists

API design decisions

Run every decision in order. Decision 1 is the headline; the rest are how you satisfy it.

1. **Headline: make it easy to use correctly, hard to use incorrectly.** *(Meyers, 97/55.)* Before the API exists, write a handful of realistic call sites — on a whiteboard, in a scratch file, in a test. The natural way to call it should be the correct way. Then ask: what mistakes will a tired caller make? Swapped argument order, forgotten cleanup, calling methods in the wrong sequence, passing a stringly-typed value that means nothing? Anticipate those, then change the *interface* (not the docs) so each one is awkward or impossible. Every later decision is a tactic for this rule.

Make wrong code look wrong

2. **Prevent errors at the call site, not in the error message.** *(Colborne, 97/66.)* An error message is a sign that communication broke down upstream. Where you can, eliminate the error condition: take an enum instead of a string, take a parsed `Url` instead of `String`, take a non-empty list type instead of `List` plus a runtime check. Where free input is unavoidable, parse leniently and report specifically. Defaults should reflect the common case. Most caller mistakes are systematic — the API drew them in — not user incompetence.

Encapsulate

3. **Encapsulate behavior, not just state.** *(Landre, 97/32.)* A type that exposes only getters and setters has pushed every business rule out into its callers, where the rule will be re-implemented inconsistently. If `Order.addItem` needs a credit check, the credit limit and the check belong on `Customer`, and `Order` asks `Customer`. Anti-pattern: an `OrderManager` / `OrderService` that holds all the logic while `Order`, `Customer`, and `Item` are records. When state and the behavior that depends on it live together, callers can't get the sequence wrong. And encapsulate *one* coherent behavior — a class with fourteen methods of which any caller uses two has the dual problem: every caller depends on a surface they don't all need. The exported surface should have one reason for callers to depend on it. *(Martin, 97/76 — SRP at the boundary.)* 4. **Don't extract a shared API until the contexts are actually shared.** *(Dahan, 97/7.)* Two call sites with the same four lines of code are not necessarily the same concept — they may be the same shape today and diverge tomorrow under different business pressures. A premature shared library ties the two callers together: every change now requires synchronizing both. Localize first; extract only when a real shared concept emerges and you can name it in the domain.

Use the type system

5. **Prefer domain-specific types to primitives.** *(Landre, 97/65.)* `ship(weight: Kilograms)` and `ship(thrust: Newtons)` cannot be confused at a call site; `ship(weight: double)` and `ship(thrust: double)` can — and the Mars Climate Orbiter is the canonical example of how that ends. In statically typed languages this becomes a compile-time guarantee; in dynamic ones, a small wrapper class plus a unit test gives you the same readability and the same encapsulation point for domain rules. 6. **Model state explicitly; reject illegal operations by type or guard.** *(Nilsson, 97/84.)* If an `Order` can be in `InProgress`, `Paid`, or `Shipped`, then `addItem` is only legal in one state and `ship` is only legal in another. Pretending the state doesn't exist (one flat class with a pile of booleans) leads to nonsense like "shipped before paid" being representable. Either split into state types, or check the current state at the start of every operation that depends on it. Method signatures should reflect what's actually callable.

API ergonomics

7. **Design vocabulary, not conveniences.** *(Hohpe, 97/19.)* `parser.processNodes(text, false)` is meaningless at the call site — the reader must consult docs to learn what `false` means. A boolean or enum flag whose value flips the meaning of t

Read more
@oribarilan
Ships with97

Agent skills distilled from the hard-won lessons of world-renowned programmers, in the spirit of "97 Things Every Programmer Should Know"

Stars on GitHub

Momentum

stars in total
25
Star history of oribarilan/97 on GitHub, a chart by star-history.com

Other skills on 97.

before-you-refactor

before-you-refactor

Use when considering, evaluating, or performing a refactor, restructure, cross-file rename, or cleanup

@oribarilan@oribarilanInstall Skill
build-deploy-and-tooling

build-deploy-and-tooli…

Use when writing, reviewing, or changing build scripts, CI workflows, deploy pipelines, repo setup, or evaluating a new…

@oribarilan@oribarilanInstall Skill
clean-code

clean-code

Use when writing or reviewing functions, classes, naming, or non-trivial logic (≥3 lines)

@oribarilan@oribarilanInstall Skill
correctness-traps

correctness-traps

Use when writing or reviewing error handling, floating-point math, concurrent code, remote calls, singletons/globals, hot-path…

@oribarilan@oribarilanInstall Skill
domain-modeling

domain-modeling

Use when introducing, reviewing, or renaming a top-level type, table, or domain concept; or choosing where state lives (in-memory…

@oribarilan@oribarilanInstall Skill
observability

observability

Use when writing or reviewing request handlers, RPCs, or background jobs for production; adding tracing, metrics, or…

@oribarilan@oribarilanInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about api-design.

Which coding agents can use api-design?
api-design installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.