API Design Review: A Staff-Level Capstone

Learn how to review an API design in five passes, find 13 planted flaws in a ticket-booking API, and practise the follow-up questions interviewers ask.

API design review: common questions

How do you review an API design?

Challenge the need first, lint the contract against your guidelines, review it as the provider (secure, buildable, evolvable) and as a consumer (clear, short, no jargon), then verify the implementation with contract and security tests.

Why should a team write API design guidelines?

They settle recurring choices like paging, errors and naming once, so APIs stay consistent across teams, newcomers design well faster, and a linter can check the rules automatically.

Related chapters

Keep learning

Overview

Challenge needs Provider review Consumer review POST /createBooking POST /events/123/holds then POST /bookings The verb belongs in the method. The path names a resource, so every resource works the same way. principle 1 John can type Priya’s ID into the body and book on her account. Who is calling is decided by the token, never by the payload. API security GET /events?city=pune&from=2026-11-01&limit=20&cursor=evt_456 Nothing to change. Plural noun, filters as query parameters, a bounded page and a cursor. This line is a decoy: reviews also confirm what is right. GET /events/123/activity?offset=40&limit=20 (live feed) GET /events/123/activity?limit=20&cursor=act_981 New rows land at the top every second, so “skip 40” shifts and the fan sees repeats or misses rows. A cursor says “after this item”. cursor paging POST /bookings/9/payments\nIdempotency-Key: 5f1c… (server stores the first result) The phone loses signal, the app retries and Priya pays twice. With a key, the second call returns the stored result instead of charging again. idempotency keys Caches, retries, dashboards and client libraries all read the status code. A 200 tells them everything worked. problem details Column names and number codes are the provider’s view. Consumers need words they understand, and the table can change without breaking them. names and types GET /events/123 → 200, no Cache-Control, no ETag Cache-Control: public, max-age=60\nETag: "e123-v7" The event page is public and read by thousands when sales open. Without cache headers every view hits the origin. caching and ETags DELETE /bookings/9 (handler checks “logged in”) allow only if booking.owner == token.sub, or role is admin Logged in is not the same as allowed. John changes 8 to 9 and cancels Priya’s booking: broken object-level authorization, the top API risk. OWASP API Top 10 GET /events/123/seats?limit=100000 One request can pull the whole seat map and tie up the database. Every input needs a bound. resource consumption Webhook-Signature: t=1793577600,v1=<HMAC-SHA256 of t.body>\n+ event id for dedupe, retries with backoff Without a signature, anyone who learns the partner’s URL can post fake cancellations. signed webhooks Old mobile apps stay installed for months. Give machine-readable notice, a long window and a migration guide, and watch v1 traffic before turning it off. Deprecation and Sunset POST /admin/refunds (same gateway, any valid token works) separate admin API, staff-only identity, scope admin:refunds Customers can guess admin paths. Function-level checks (role or scope) must guard every admin action, not just a hidden menu. Lab: review the draft. Click each line you think is wrong. · review complete 0.5rem 0.7rem 0 0.6rem 0.6rem Fine as it is caught by checked in Reveal all Start over Protocol fits each caller: REST for public, gRPC inside, events for partners Nouns in paths, methods carry the action, status codes are standard Safe and idempotent methods used as HTTP defines them If GraphQL: query depth and cost limits, persisted queries If gRPC: deadlines, retries only on idempotent calls, proto field numbers never reused Consistent names, cases and shapes across all endpoints Money as integer minor units + ISO 4217 currency, ISO 8601 times, errors as problem details Cache headers on public reads, ETag + If-Match on updates OpenAPI is the source of truth, linted in CI, contract-tested against the code Right OAuth flow per client, narrow scopes, tokens validated fully Gateway quotas, canary releases, RED metrics per endpoint Need challenged first: ask why until the real goal shows Guidelines written down and a linter runs them on every change Security tests prove John can’t read Priya’s booking How do you stop double booking on retries? How would partners get notified of cancellations? How do you version without breaking the mobile app? Most change is additive, which needs no new version: new optional fields, new endpoints. Clients ignore unknown fields. For a true break we ship /v2 beside /v1, send Deprecation and Sunset headers, watch v1 traffic by app version, and force-upgrade only the oldest builds. Why REST outside and gRPC inside? Outside, callers are browsers, apps and partners we don’t control: REST over HTTP gives caching, easy debugging and universal tooling. Inside, we own both ends: gRPC gives typed contracts, compact binary messages, streaming and deadlines that pass down the call chain. How do you stop scalper bots? Layers, because no single control is enough: per-user and per-IP rate limits at the gateway (429 + Retry-After), a ticket limit per account per event, short seat holds that expire, a waiting room when sales open, and bot detection at the edge. The API enforces the same limits as the website, so bots gain nothing by calling it directly. What do you cache? Where do you check that John can’t cancel Priya’s booking? In the booking service, on every request, by comparing the booking’s owner with the token’s subject. The gateway checks the token and scope, but only the service knows who owns booking 9. And a security test proves it. 0.6rem 0.8rem

Capstone: a staff-level API design review

The seventeen chapters before this one gave you the parts. This one puts them to work the way a senior engineer does: you get a draft API for the ticket site, you review it in five passes, you find the planted mistakes, and you end with the design you would defend in an interview.

Why APIs get reviewed, and why teams write guidelines

An API is cheap to change on a whiteboard and expensive once apps ship against it. A review moves the mistakes to the cheap end. Design is only one stop in the life of an API: is an API the answer? the contract and evolve v1 after v2 Reviews happen at several of these stops, not only after design. With many teams, the same questions come back every week: how do we page lists, what does an error look like, how do we name dates? Guidelines answer them once, so reviews argue about the product instead of style. Good guidelines have three layers: Reference rules the what Allowed methods, status codes and headers, path format, error shape, paging, and a shared vocabulary. Use-case recipes the how “To create something, do this.” “To page a list, do this.” Ready to copy, with examples. Design process the steps How to go from a need to a contract: goals canvas, draft, lint, review, test. Keep them alive: write down why each rule exists, version them, build them with the people who use them, and allow a documented exception. A rulebook enforced by an “API police” gets ignored.

The five passes of a review

Challenge the need why? who? context? against guidelines Provider view secure, buildable, evolvable Consumer view clear, short, no jargon Verify the build tests prove it Lint checks the form Paths, methods, status codes, names, types, required fields, a security scheme on every operation, breaking changes. Mostly automatic. People check the substance Does it meet the need? Is it safe and buildable? Would a stranger understand it? A linter can’t answer these. Provider view: does the flow meet every need, is every operation secured, can the backend deliver it at the expected speed, will it grow? Consumer view: pretend you know nothing. Are names free of internal jargon, are only the truly needed inputs required, is the flow short, do errors say what to do? Then show it to a real consumer. Verify: contract tests prove the code matches the spec, and security tests prove each user sees only their own data.

Review lab: the draft ticket API

A teammate posts this draft for the booking flow. Thirteen lines hide a problem and one is fine. Each answer names the review pass that should catch it. Notice the spread: lint catches the form errors cheaply, but the two most dangerous ones, ownership and admin access, only show up when someone tests the running code.

The corrected design

Every fix from the lab, assembled. Three audiences, three styles, one set of scopes and limits: Public REST apps and web Cacheable reads, idempotent writes, OAuth with PKCE. Internal gRPC service to service Typed contracts, mTLS in the mesh, deadlines on every call. Partner webhooks other companies Signed events, retries, dedupe by event id. Two details that set a staff answer apart: the client never sends the amount to pay (the server computes it from the booking), and a seat hold with an expiry separates “choosing seats” from “paying”, so a slow card form never locks seats forever.

The staff-level checklist

One line per habit, grouped by the five parts of this course. Each links back to where it is taught. 0.5rem 0 0

Interview pacing: five minutes or twenty

API step in a system design round About 5 minutes. List 4–6 core endpoints, name the protocol, call out one or two hard parts. Then move on to data and scale. API design deep dive About 20 minutes. The interviewer wants the contract itself: shapes, errors, paging, retries, auth, versioning and limits. “Public REST over HTTPS: GET /events with a cursor, GET /events/:id cached at the CDN, POST /events/:id/holds, then POST /bookings with an Idempotency-Key. The user comes from the OAuth token. Internally the booking service talks gRPC to inventory and payments. The tricky bit is double booking: the hold is a conditional write, so the second buyer gets a 409.” 0–3 min consumers and goals resources and endpoints payloads, errors, paging retries, auth, limits versioning, webhooks, ops A 20-minute deep dive, in five slices. Leave time for questions. “Consumers are fans on the app, venue managers like Asha, and partner sites. I’ll design the fan booking flow first, then partners. Errors are RFC 9457 problem details, money is integer paise plus a currency code, lists use cursors capped at 100. Every write takes an Idempotency-Key. Scopes split fans, venue managers and staff, and ownership is checked in the service. Breaking changes go to /v2 with Deprecation and Sunset headers, and partners get signed webhooks.”

Follow-up questions interviewers ask

Answer in two or three sentences, name the mechanism, then offer to go deeper. 0.5rem 0 0

Where to go next

Next, three chapters replay the whole course on a real API: the Spotify Web API , with GitHub, Stripe and Meta filling the gaps. Then practise the same review on full designs. The hotel reservation design (opens in a new tab) has the same hold, double-booking and idempotency problems with rooms instead of seats, and the digital wallet design (opens in a new tab) pushes money handling, idempotent transfers and auditing further. Run the five passes on each before reading the answer. “Before I call this done, I’d review it in five passes: is this the real need, does it pass our lint rules, is it secure and buildable, would a new developer understand it, and do tests prove John can’t touch Priya’s booking.” Every chapter of this course mapped to a real endpoint at Spotify, GitHub, Stripe or Meta. The course, mapped to real APIs

More courses

  • Jobs, linked to lessons — Browse jobs where every requirement links to the chapter that teaches it. See how ready you are, how long the gap takes to close, and which listings look stale.
  • Match a job post to courses — Paste a job post. Each skill is matched to a track here and put in learning order, foundations first.
  • React from open source with patterns — Patterns extracted from Material UI, Radix, React Aria, React Hook Form, Redux Toolkit, TanStack, and Next.js — not slides about hooks.
  • Database in depth — How a query becomes work. Indexes, isolation, locks, and the plans that explain why a page is slow — not an ORM tour.
  • Build a real API with NestJS — One ticket-booking API, built in order: a plain Node server first, then NestJS modules, validation, a real database, and the features real clients lean on.
  • Microservices — Microservice patterns one diagram at a time: how to split a monolith, how services talk, and how data stays consistent without distributed transactions.
  • Kafka — A beginner-to-senior path through event logs, reliable delivery, production operations, and disaster recovery.
  • AI engineering for software engineers — Eight free reads, in order, for software engineers moving into AI: when to build an agent, how to build one, how to test it, and how the model works underneath.
  • Frontend system design — How a UI is sliced when it is a product, not a page: ownership of state, cache, routes, and what happens when the network lies.
  • TypeScript in depth — The type system as a design tool. Inference, narrowing, generics, and the errors that mean the model is wrong — not a catalog of utility types.
  • CSS in depth — See the box, then the page. Visual chapters from the box model through Flexbox surprises, Grid, selectors, and units, then backgrounds, shapes, and visual effects, each with a playground.
  • Accessibility — Foundations first (WCAG in plain words, semantic HTML, lists, alt text), then hands-on HTML labs, a full pass of WAI-ARIA 1.3, and a final fix-the-page challenge.
  • Docker — What an image is, why layers cache, and how containers find each other on a network.
  • Redis — Fifteen Redis patterns, one picture each: cache, session, lock, counter, rate limiter, queue, and when Redis is the wrong tool.
  • Kubernetes — Kubernetes from zero to operators: why it exists, Pods, Deployments and Services, then configuration, networking, storage, scheduling, scaling and security.