Third phase of Arc 2. APIs as a contract.

APIs are the surface where your service meets the world. Designed well, they’re easy to use, survive evolution, and document themselves. Designed badly, they leak implementation details, break consumers on every release, and require tribal knowledge to use. This phase teaches the discipline: HTTP at depth (because most APIs are HTTP-based), REST as more than “URLs with verbs,” gRPC where it earns its weight, and the versioning hygiene that lets your API outlive its first version.

By phase end your Arc 2 service exposes a clean REST API with proper HTTP semantics, an OpenAPI contract, and a gRPC alternative for one or two endpoints. You’ve designed at least one endpoint that demonstrates Richardson Maturity Level 3 (hypermedia). You’ve thought through versioning. The API reads like a deliberately designed thing, not an accidental one.


Prerequisites

  • Phase 10 complete; Redis caching working
  • Your service has a basic HTTP interface (probably from earlier hacking); this phase makes it deliberate
  • You accept: a well-designed API is engineering. “Just expose the database” is not an API.

Why this phase exists

Most backend engineers write APIs by accident: an endpoint per page, an endpoint per feature, no versioning strategy, error responses invented on the spot. The API works for the first six months and becomes a nightmare in year three when the third client integration reveals that nothing is consistent. Senior engineers design APIs deliberately, treating each endpoint as a long-lived contract with consumers.

This phase installs the deliberate-design discipline. By phase end you can defend your API’s shape against a thorough code review.


The pattern-first frame

Same eight steps as every phase.


1. PROBLEM

Other software needs to interact with your service. The interaction has shape: requests, responses, error states, evolution over time. The shape needs to be: predictable (consumers can guess), discoverable (consumers can explore), versioned (you can change it without breaking clients), and documented (consumers can read it).

That’s the API design problem. REST is the dominant pattern (using HTTP semantics as the structure). gRPC is the strongly-typed binary alternative for service-to-service. GraphQL is a query-language alternative. WebSockets and SSE are the streaming variants. Each fits different scenarios.


2. PRINCIPLES

2.1 HTTP semantics at depth

HTTP defines methods (GET, POST, PUT, PATCH, DELETE), status codes (2xx, 3xx, 4xx, 5xx), headers (caching, content negotiation, auth), and the request/response lifecycle. Most APIs misuse HTTP. The well-designed ones lean into HTTP, treating it as their grammar.

Investigate:

2.2 REST and Richardson Maturity

Roy Fielding’s REST is more than “URLs with verbs.” Richardson Maturity Levels: Level 0 (one URL, RPC over HTTP), Level 1 (resources), Level 2 (resources + HTTP verbs), Level 3 (hypermedia / HATEOAS). Most “REST” APIs sit at Level 2; Level 3 is rare in practice.

→ Pattern: rest-as-pattern

Investigate:

2.3 Resource modeling

REST APIs are organized around resources, not actions. POST /users creates a user. DELETE /users/42 deletes one. POST /users/42/cancel is a resource (the cancellation action). The resource-noun mindset is where REST design lives.

Investigate:

2.4 gRPC and RPC

gRPC is binary, strongly-typed (Protobuf), HTTP/2-based RPC. It’s faster than REST at the wire level and gives you generated clients in every language. The cost: less discoverability (no curl-able URLs), tooling lock-in, less browser-friendly.

→ Pattern: grpc-as-pattern

Investigate:

2.5 API versioning

Your API will change. Versioning is how you change it without breaking consumers. Strategies: URL versioning (/v1/users), header versioning (Accept: application/vnd.api.v1+json), query parameter (?v=1). Each has costs.

→ Pattern: api-versioning

Investigate:

2.6 OpenAPI as a contract

OpenAPI (the spec formerly known as Swagger) lets you describe your REST API in YAML or JSON. Generated docs, generated clients, contract tests: all flow from a good OpenAPI document.

Investigate:


3. TRADE-OFFS

DecisionOptionsCost
API styleREST; gRPC; GraphQL; tRPCREST: ubiquitous, HTTP-native. gRPC: fast, typed, service-to-service. GraphQL: flexible queries, complex backend. tRPC: TypeScript-tight, monorepo-friendly.
Framework (Python)FastAPI; Litestar; Flask; Django RESTFastAPI: modern, async, OpenAPI native. Litestar: similar. Flask: simple, mature. Django REST: batteries-included.
Framework (Go)chi + stdlib; gin; echo; connect-go (for connect-rpc / gRPC)chi: minimal, idiomatic. gin/echo: middleware-rich. connect-go: gRPC + REST simultaneously.
VersioningURL (/v1/); header; query param; no versioningURL: ubiquitous. Header: pure REST. Query: hacky. None: rapid breaking changes inevitable.
Error formatRFC 7807 (Problem Details for HTTP APIs); custom JSON; google.rpc.StatusRFC 7807: standard. Custom: works, less interoperable. google.rpc: gRPC-style.

4. TOOLS (as of 2026-06)

REST frameworks

gRPC

Tooling

Reading


5. MASTERY: REST + gRPC for your service

5.1 The deliverable

Your Arc 2 service now exposes:

5.2 Operational depth checklist

[ ] Audit your existing endpoints; identify ones using wrong HTTP verbs or status codes
[ ] Apply RFC 7807 (or similar) error format across all endpoints
[ ] Hand-author or generate an OpenAPI 3.1 spec; verify it matches reality
[ ] Add one gRPC endpoint; define the Protobuf schema; generate clients in both Python and Go
[ ] Use `buf` to lint Protobuf and catch breaking changes
[ ] Run `Schemathesis` (or equivalent) contract tests against your REST API
[ ] Implement API versioning; document the version sunset policy
[ ] Add `Accept-Language` or `Accept` content negotiation for one endpoint
[ ] Document the API in the repo README with curl examples
[ ] Read the OpenAPI spec of a well-known API (e.g., Stripe) for reference

6. COMPARE: GraphQL or tRPC

Pick one:

400-word reflection on what each style optimizes.


7. OPERATE


8. CONTRIBUTE


What ships from this phase


Learning loop cadence

Week 1     PROBLEM + PRINCIPLES 2.1 (HTTP semantics)
           Audit existing endpoints; fix status codes + verbs

Week 2     PRINCIPLES 2.2-2.3 (REST, resource modeling)
           Refactor URLs to resource model; RFC 7807 errors

Week 3     PRINCIPLES 2.4 (gRPC)
           Add first gRPC endpoint; Protobuf schema

Week 4     PRINCIPLES 2.5-2.6 (versioning, OpenAPI)
           Versioning strategy; OpenAPI spec authored
           Contract testing with Schemathesis

Week 5     COMPARE: GraphQL or tRPC
           chronicle runbooks

Week 6-7   OPERATE + CONTRIBUTE
           Exit Test

Validation criteria

[ ] Arc 2 service exposes clean REST API + OpenAPI spec
[ ] At least one gRPC endpoint operational
[ ] Versioning strategy applied
[ ] Contract tests running in CI
[ ] All 10 operational depth checks
[ ] Compare reflection (400 words)
[ ] 2-3 API runbooks
[ ] 1+ ADR
[ ] Pattern entries deepened STUB → OUTLINE:
    - rest-as-pattern
    - grpc-as-pattern
    - api-versioning
[ ] Exit Test passed

Exit Test

Time: 2.5 hours.

Part 1: Design + Build (90 min)

Design a new endpoint (spec provided) using REST principles. Pick the right HTTP method, status codes, error format. Add it to your service. Update the OpenAPI spec. Add at least one Schemathesis test.

Part 2: Diagnose (45 min)

An API scenario (provided: e.g., “consumers report random 500 errors only when sending PATCH requests with Content-Type: application/merge-patch+json”). Identify root cause.

Part 3: Articulate (15 min)

~400 words: “When would you reach for gRPC over REST? Walk through one endpoint in your service that’s a good gRPC candidate and one that should stay REST.”


Anti-patterns

Anti-patternWhy
Returning 200 for everythingStatus codes ARE part of the API contract
GET /users/getUser?id=42 styleRPC-over-HTTP. You’re paying REST costs without REST benefits.
No versioning strategyEvery change becomes a breaking change
Auto-generated OpenAPI without reviewDrift between spec and code is a common bug source
“REST” without thinking about resourcesVerbs in URLs (/createUser) is the giveaway

Patterns touched this phase


→ Next: Phase 12: Authentication & Authorization