Core Entities & the API Sketch
Turn requirement nouns into design-relevant entities and one endpoint per requirement, with protocol, idempotency, and auth chosen deliberately.
Two artifacts that anchor the whole design
Once the problem is scoped, estimated, and its NFRs are set, the abstract problem becomes concrete in two artifacts: the core entities (the nouns your system stores) and the API sketch (the interface clients call). Doing these two before you draw any boxes anchors the whole design, because every service, cache, and datastore you add later exists to serve some entity through some endpoint.
Core entities: nouns, not schemas
Core entities come straight from the nouns in your functional requirements. For a URL shortener, "users
can create a short link" and "users can be redirected" give you the entity ShortLink, and if you support
accounts, User. The discipline here is to list only the fields that matter for the design, not a fully
normalized schema. A ShortLink needs code (the short slug), long_url, created_at, maybe
owner_id and expires_at. It does not need you to enumerate every column and index up front; that
is a rabbit hole that burns time and reveals nothing about your systems thinking. You are naming
entities to establish the data model's shape, not writing a migration.
Interview nuance: Resist fully normalizing the schema at this stage. Interviewers read a candidate who spends five minutes on third-normal-form column design as someone who cannot tell the load-bearing decisions from the details. Name the entity, list the three or four fields that drive the design (the ones that get indexed, sharded, or looked up), and move on.
The API sketch: one endpoint per requirement
The API sketch is one endpoint per functional requirement, with request and response shapes concrete enough to reveal the data flow. For a URL shortener:
POST /links
req: { "long_url": "https://...", "custom_alias?": "promo" }
resp: { "code": "aZ3xK", "short_url": "https://sho.rt/aZ3xK" }
(idempotency-key header so retrying the same long_url is stable)
GET /{code}
resp: 302 redirect, Location: <long_url>
(302 not 301, so you keep control of the link and can gather analytics;
301 is cached by browsers and you lose the redirect and the click data)
The 301-versus-302 choice is a classic probe: 301 (permanent) is cached hard by browsers and CDNs, which is great for read latency but means the browser stops asking your service, so you lose click analytics and can never repoint the link. 302 (temporary) keeps every redirect flowing through you. Most shorteners choose 302 to retain control and analytics, accepting the extra request. Knowing that tradeoff is the point.
Choose the protocol deliberately
REST over HTTP is the right default for a public-facing API: it is cacheable, universally understood, and works through any client. Use gRPC for internal service-to-service calls where you control both ends and want lower latency and typed contracts (a Protobuf schema, binary framing, HTTP/2 multiplexing). Use a streaming protocol (WebSocket, Server-Sent Events, or gRPC streaming) when the server must push, like a live location feed or a chat. State which and why: "REST for the public create and redirect endpoints, gRPC between the API gateway and the internal link service."
Pick the right protocol for each call in a ride-sharing system.
Two boundary concerns belong in the API sketch because they are easy to forget and interviewers look
for them. Idempotency: for creates, an idempotency key makes retries safe (the client can retry a
timed-out POST /links without creating duplicate codes for the same URL). Pagination and auth:
any endpoint returning a list needs cursor-based pagination (?cursor=...&limit=25), and any write or
private read needs an auth token at the boundary. Mentioning where these live shows you have designed
real APIs, not just toy ones.
Recap: Turn requirement nouns into entities with only the design-relevant fields, define one endpoint per requirement with concrete request/response shapes, choose REST vs gRPC vs streaming deliberately, and place idempotency, pagination, and auth at the boundary.
Apply
Your turn
The task this lesson builds to.
Define the core entities and the REST/RPC endpoints (create, redirect) for a URL shortener with request/response shapes, and note where you would choose REST vs gRPC vs a stream.
Think about
- Which nouns in the requirements become entities, and which fields actually matter?
- What is the minimal endpoint set, one per functional requirement?
- Where do idempotency keys, pagination, and auth belong at the boundary?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Sketch the core entities and the API for requesting a ride and streaming live driver location to the rider in a ride-hailing app like Lyft. Specify where you use REST, where you use a streaming protocol, and why.
Think about
- Which fields on Ride and Driver actually drive the design (state machine, location freshness, matching keys)?
- Which interaction is request/response and which is a continuous server push, and what protocol fits each?
- Where does an idempotency key protect the rider from double-charging over a flaky mobile network?