Skip to main content
ClaudeWave
Skill708 repo starsupdated 18d ago

old-coder-api

Design, change, or review an HTTP/JSON API surface — endpoints, request/response shapes, authentication and authorization, pagination, idempotency, rate limits, versioning, and deprecations. Use when adding or modifying an HTTP endpoint, reviewing an OpenAPI spec or HTTP route diff, or deciding whether an HTTP API change breaks consumers. Do not use as a protocol-compatibility checklist for gRPC/protobuf, GraphQL, WebSockets, or other non-HTTP/JSON interfaces.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/AmazingAng/old-coder /tmp/old-coder-api && cp -r /tmp/old-coder-api/skills/old-coder-api ~/.claude/skills/old-coder-api
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# old-coder-api

Inspired by Sean Goedecke, *Everything I know about good API design* (2025-08-24).

This skill covers HTTP/JSON contract and operability concerns. Its compatibility rules assume JSON consumers. For gRPC/protobuf, GraphQL, WebSockets, or another protocol, apply the transport-independent principles only alongside that protocol's own compatibility rules. This is not a substitute for a full application-security review.

**Good APIs are boring.** For the people who build them, an API is a product. For the people who use them, it is a tool in the way of something else. Every minute a consumer spends thinking about your API instead of their goal is waste. An interesting API is a bad API — or would be a better one if it were less interesting.

Two failure modes an agent falls into by default, and this skill exists to stop both:

1. **Inventing.** Producing a clever, bespoke interface where the boring conventional one would do.
2. **Breaking.** Renaming, restructuring, or tightening a field because it reads better now — and silently breaking every downstream caller.

**Composition with `old-coder`:** when both skills apply, this skill owns the
HTTP/JSON contract while `old-coder` owns workflow order, SPEC approval, the
gauntlet, and EVIDENCE. Run Step 0 and the gates before SPEC approval; put the
surviving API constraints and risks into SPEC and verify them through the
gauntlet. For review-only work with no implementation, use this skill's review
format without manufacturing a development loop.

## Step 0 — establish scope before designing anything

Answer these three, out loud, before writing a route:

| Question | Why it changes the work |
|---|---|
| **Public or internal?** Can you ship code for every consumer? | Internal: breaking changes are affordable, complex authentication is fine, non-engineer ergonomics don't matter. Public: none of that holds. |
| **Existing surface or greenfield?** | Existing → run `references/breaking-changes.md` **first**; compatibility outranks every improvement below. |
| **Does the product's resource model support this API?** | API design tracks the product's basic resources. If the resources are awkward (state machines with no name, records that only exist inside a job, parent/child relations that aren't modeled), the API will be awkward no matter how carefully you design it. Say so instead of papering over it. |

**Honesty rule for step 0:** when the ugliness comes from the underlying model, name it and propose the model fix as the real option. A background-job-polling interface bolted onto a read that *should* be a read is how the worst APIs happen — technical constraints that the UI hides get laid bare in the API, forcing consumers to understand far more of your system than they should have to.

## The gates

Run every gate. Use **✓** only for a verified pass, **✗ + concrete fix** for a verified failure, **N/A + reason** only when the gate truly does not apply, and **? + reason** when it remains unverified. Never skip silently.

### 1. Boring
A competent consumer should be able to guess this endpoint before reading any docs.
- Resources are the product's nouns (`/issues`, `/projects`, `/users`), plural, stable.
- Standard verbs and status codes, following the established convention of this API. Use `400` for a general client error; use `422` only when the content type and syntax are valid but the contained instructions cannot be processed. Use `404` for missing and `429` for rate-limited.
- Standard field names: `id`, `created_at`, `next_page`, `url`. Match the names the rest of *this* API already uses — internal consistency beats external convention when they conflict.
- REST + JSON unless there's a reason. An established, internally consistent HTTP RPC surface can also be boring; do not rename it to resource paths for REST purity. Don't relitigate HATEOAS or JSON-vs-anything; it isn't important.
- **Anything surprising needs a written justification line.** If you can't write one, make it boring.

### 2. Don't break userspace
Applies only to changes on an existing surface. Full matrix in `references/breaking-changes.md`.
- Additive is fine: new endpoints, new optional params, **new response fields**. Consumers are expected to ignore unknown fields.
- Removing a field, renaming it, changing its type, moving it (`user.address` → `user.details.address`), narrowing an enum, or tightening validation is a break. Don't, even if it's neater. The HTTP `referer` header is a misspelling and it is still there.
- If a break is genuinely unavoidable: versioning, as a **last resort** — see the reference.

### 3. Authentication: make the simplest safe path easy
Many server-to-server integrations start life as a `curl` or a 20-line script. For developer-facing server-to-server APIs, default to simple, scoped, revocable API keys.
- Use OAuth or another short-lived or sender-constrained flow instead for browser/mobile clients, user-delegated access, high-sensitivity data, or environments where policy requires it. Do not ship long-lived bearer credentials into those clients.
- For every credential type, define scope, rotation, revocation, secure transport, and a way to identify or disable the credential during an incident.
- N/A for internal credential ergonomics: use the mechanism the infrastructure already provides (mTLS, workload identity, service tokens), while still verifying its operational controls.

### 4. Authorization: enforce who may do what to which resource
Authentication identifies a caller; it does not authorize an action. For every endpoint, identify the actor, action, resource, and tenant boundary.
- Enforce authorization server-side on the resolved resource. Do not trust a caller-supplied `tenant_id`, owner ID, role, or scope without checking it against the authenticated principal.
- Apply the same checks to list, search, bulk, export, nested-resource, and indirect lookup paths; filtering after fetching is not an authorization boundary.
- N/A