Start

Course · 02

What really happens between a click and a reply?

This page follows one request from the address bar to the server and back, then shows how to design the API that answers it: REST, gRPC, GraphQL, and the ways to push live updates.

TL;DR

Every request pays for round trips (DNS, TCP, TLS) before any data moves, and your API design decides how safe it is to retry.

  • A fresh HTTPS request over TCP + TLS 1.3 needs 3 round trips before the first byte. HTTP/3 (QUIC) needs 2.
  • HTTP/2 fixed head-of-line blocking at the HTTP layer, but not in TCP. HTTP/3 moves to UDP-based QUIC to fix that too.
  • GET, PUT and DELETE are idempotent by RFC 9110; POST is not. Make POST safe to retry with an idempotency key.
  • For live updates: SSE for server-to-client only, WebSockets for two-way, long polling as the fallback.
1RTT
TLS 1.3 full handshake (RFC 8446)
6
SSE connections per browser and domain without HTTP/2 (MDN)
13
root name servers, run by 12 organisations (root-servers.org)

—Words you need

Every hard word on this page, in plain words glossary

DNS
The internet's phone book: turns names into IP addresses.
IP address
A machine's number on the network, like 93.184.216.34.
Resolver
The DNS helper that does the lookup for you.
RTT
Round-trip time: there and back once.
TCP
Reliable, in-order delivery. Resends lost pieces.
UDP
Fire-and-forget packets. No order, no resend.
TLS
Encryption layer that puts the "S" in HTTPS.
QUIC
New transport on UDP with TLS built in. Carries HTTP/3.
Head-of-line blocking
One stuck item holds up everything queued behind it.
Idempotent
Doing it twice has the same effect as doing it once.
Cursor
A bookmark saying "continue after this item".
RPC
Remote procedure call: call a function on another machine.
Protobuf
Google's compact binary format for structured data.
SSE
Server-sent events: server streams updates over one HTTP response.
WebSocket
A two-way pipe that stays open between browser and server.

The journey

01What happens when you type a URL

DNS, then TCP, then TLS, then HTTP interview favourite

You type https://a.com and press Enter. Four things happen in order. First the browser finds the IP address (DNS). Then it opens a connection (TCP), makes it secret (TLS), and finally asks for the page (HTTP).

Part 1: finding the address (DNS)

The browser checks its own cache and the OS cache first. If the name is not there, it asks a recursive resolver (usually your ISP's, or a public one). The resolver walks down the tree: root → top-level domain (.com) → the domain's own authoritative server, which holds the real answer.

DNS resolution chain The browser asks a resolver; the resolver asks the root, then the .com server, then the domain's authoritative server, which returns the IP address and TTL. A.COM? A.COM? ASK .COM A.COM? ASK OWNER A.COM? IP + TTL IP Browser+ OS cache Resolverrecursive Root13 names Top level.com Ownerauthority LEGEND Question Referral / answer Final answer
Time runs down. The resolver caches every answer for its TTL, so most lookups stop at step one.

Part 2: connecting, securing, asking

TCP handshake, TLS 1.3 handshake, then the HTTP request Browser and server do the TCP three-way handshake (one round trip), the TLS 1.3 handshake (one round trip), then the browser sends GET and gets 200 OK. TCP1 RTT TLS 1.31 RTT HTTP1 RTT SYN SYN-ACK ACK CLIENT HELLO SERVER HELLO FINISHED, GET 200 OK Browserclient Serverport 443 LEGEND Client sends Server replies First useful byte
Three round trips before the page arrives: TCP, TLS 1.3, then the request itself (the GET rides along with TLS Finished).

Round trips before the first response byte (new connection, DNS not counted)

Fewer is faster. On a 150 ms ocean link, each round trip costs 150 ms. Hover for the breakdown.

TCP + TLS 1.2
4
TCP + TLS 1.3
3
QUIC (HTTP/3)
2
QUIC 0-RTT resume
1
Show the full table
SetupTransport RTTSecurity RTTRequest RTTTotal
TCP + TLS 1.21214
TCP + TLS 1.31113
QUIC (HTTP/3)1 (combined)12
QUIC 0-RTT resumption011
In everyday words
Calling a shop: first look up the number (DNS), then dial and hear "hello" (TCP), then agree on a secret language (TLS), and only then place your order (HTTP).
Actually
TCP's "three-way handshake" (SYN, SYN-ACK, ACK) is in RFC 9293 §3.5. TLS 1.3's full handshake (RFC 8446 §2, Figure 1) is one round trip: ClientHello with a key share, then ServerHello through server Finished; the client can send app data right after its Finished. RFC 8446 also adds 0-RTT, which "saves a round trip" for resumed sessions but can be replayed, so only safe (idempotent) requests should use it. QUIC (RFC 9000, with TLS in RFC 9001) does the transport and TLS handshake together in one "1-RTT handshake". The TLS 1.2 two-round-trip count is from RFC 5246's handshake flow. The round-trip totals above are my arithmetic from those flows, ignoring DNS and TCP Fast Open.

Round trips, not bandwidth, usually decide how fast a page starts. That is why we reuse connections, use CDNs close to users, and prefer TLS 1.3 / HTTP/3.

Sources: RFC 1034 (DNS); RFC 9293 §3.5 (TCP handshake); RFC 8446 §2 (TLS 1.3); RFC 5246 (TLS 1.2); RFC 9001 (QUIC-TLS); root-servers.org

02DNS records and TTL

A, AAAA, CNAME, and how long answers are cached core

RecordMapsExampleDefined in
AName → IPv4 addressa.com A 93.184.216.34RFC 1035
AAAAName → IPv6 addressa.com AAAA 2606:2800::1RFC 3596
CNAMEName → another name (alias)www.a.com CNAME a.cdn.netRFC 1034
NSZone → its authoritative serversa.com NS ns1.host.netRFC 1035
MXDomain → mail servera.com MX 10 mail.a.comRFC 1035
TXTName → free text (domain proofs, SPF)a.com TXT "v=spf1 ..."RFC 1035

TTL (time to live) is a number of seconds on every record. Resolvers may cache the answer for that long. Short TTL means changes spread fast but more lookups. Long TTL means fewer lookups but slow changes.

In everyday words
A CNAME is a forwarding note: "for www.a.com, ask for a.cdn.net instead". The TTL is the "best before" date on a saved phone number.
Actually
RFC 1034 §3.6.2 says: "If a CNAME RR is present at a node, no other data should be present". The zone apex (a.com itself) must hold SOA and NS records, so a plain CNAME cannot sit at the apex. That is why DNS providers offer non-standard "ALIAS" or "flattening" records for apex-to-CDN setups. The TTL field is a 32-bit number of seconds (RFC 1035 §3.2.1). Note that some resolvers and browsers enforce their own minimum or maximum cache times, so a 30-second TTL does not guarantee a 30-second switch-over.

Before a planned migration, lower the TTL a day ahead, so the switch spreads in minutes instead of hours.

Sources: RFC 1034 §3.6.2; RFC 1035 §3.2.1; RFC 3596 (AAAA)

03TCP vs UDP

Reliable stream vs fast packets core

FeatureTCP (RFC 9293)UDP (RFC 768)
Connection setupYes, 3-way handshakeNo, just send
Delivery guaranteeYes, resends lost dataNo
Order guaranteeYes, one ordered byte streamNo
Flow and congestion controlYesNo (app must add it)
Header size20 bytes minimum8 bytes
Used byHTTP/1.1, HTTP/2, databases, SSHDNS queries, video calls, games, QUIC (HTTP/3)
In everyday words
TCP is registered mail: signed for, numbered, resent if lost. UDP is shouting across a room: quick, but if someone missed a word, it is gone.
Actually
RFC 768 describes UDP as giving "a minimum of protocol mechanism" with "delivery and duplicate protection" not guaranteed; its header is four 16-bit fields (8 bytes). TCP's minimum header is 20 bytes (five 32-bit words, RFC 9293 §3.1). Because TCP hands the app one ordered stream, a single lost packet makes all later bytes wait until it is resent. For live video, an old frame is useless, so UDP lets the app skip it.

Pick TCP when every byte must arrive in order. Pick UDP when fresh data beats complete data, or when you build your own reliability on top (like QUIC).

Sources: RFC 9293 (TCP); RFC 768 (UDP)

04HTTP/1.1 vs HTTP/2 vs HTTP/3

Same meaning, different wire deep dive interview favourite

All three versions share the same semantics (methods, status codes, headers), defined once in RFC 9110. What changes is how bytes travel.

HTTP/2 and HTTP/3 protocol stacks HTTP/2 runs over TLS over TCP, where one lost packet stalls all streams; HTTP/3 runs over QUIC, which has TLS 1.3 built in and runs over UDP. HTTP/2 STACK HTTP/3 STACK HTTP/2many streams, 1 connection TLS1.2 or 1.3 TCP1 lost packet stalls all HTTP/3streams mapped to QUIC QUICstreams + TLS 1.3 inside UDPno order of its own LEGEND Where head-of-line blocking lives Separate security layer
HTTP/3 moves ordering per stream into QUIC, so one lost packet only delays its own stream.
FeatureHTTP/1.1 (RFC 9112)HTTP/2 (RFC 9113)HTTP/3 (RFC 9114)
Format on the wireTextBinary framesBinary frames
Requests per connection at onceOne at a time (pipelining rarely used)Many streams multiplexedMany streams multiplexed
TransportTCPTCPQUIC over UDP
Head-of-line blockingAt HTTP layer and TCPFixed at HTTP layer; still in TCPFixed per stream
Header compressionNoneHPACK (RFC 7541)QPACK (RFC 9204)
Connection survives IP change (Wi-Fi → 4G)NoNoYes, connection IDs and migration (RFC 9000 §9)
In everyday words
HTTP/1.1 is one checkout lane where each customer waits for the one ahead. HTTP/2 is many customers sharing one conveyor belt; if the belt jams, everyone waits. HTTP/3 gives each customer their own small belt.
Actually
RFC 9113 says HTTP/1.1 pipelining "only partially addressed request concurrency and still suffers from application-layer head-of-line blocking", and admits "TCP head-of-line blocking is not addressed by this protocol". RFC 9000 lists avoiding head-of-line blocking as a benefit of QUIC: streams are delivered independently. QUIC also identifies a connection by a connection ID instead of the IP/port pair, which enables connection migration (RFC 9000 §9). Because HTTP/1.1 handles one request per connection at a time, browsers open several connections per host; MDN mentions a per-browser, per-domain limit of 6 in the SSE context.

HTTP/2 is the default between services; HTTP/3 helps most on lossy mobile networks. Your API design does not change; only the transport does.

Sources: RFC 9110; RFC 9112; RFC 9113 §1; RFC 9114; RFC 9000 §9; RFC 7541; RFC 9204

REST APIs

05REST design

Resources as nouns, HTTP methods as verbs core

REST is a style for web APIs. You name resources (things) with URLs, like /users/42/orders. You act on them with HTTP methods (verbs). Use plural nouns, not verbs in the URL: POST /orders, not /createOrder.

Methods: safe and idempotent

Safe means read-only: the server state should not change. Idempotent means sending it twice has the same effect as once. Idempotent requests are safe to retry after a timeout.

MethodTypical useSafeIdempotent
GETRead a resource or listYesYes
HEADLike GET, headers onlyYesYes
OPTIONSAsk what is allowed (CORS preflight)YesYes
PUTReplace a resource at a known URLNoYes
DELETERemove a resourceNoYes
POSTCreate in a collection, or run an actionNoNo
PATCHChange part of a resourceNoNo (RFC 5789)

Status codes you should know

CodeMeaningWhen to return it
200 OKWorkedSuccessful GET, PUT, PATCH
201 CreatedNew resource madePOST that created something; add a Location header
204 No ContentWorked, nothing to sendDELETE
304 Not ModifiedYour cached copy is still goodConditional GET with ETag
400 Bad RequestMalformed requestBad JSON, missing field
401 / 403Not logged in / not allowedMissing token / wrong permissions
404 Not FoundNo such resourceUnknown ID
409 ConflictClashes with current stateVersion conflict, duplicate unique key
422 Unprocessable ContentValid format, invalid meaningValidation rule failed
429 Too Many RequestsRate limitedAdd Retry-After (RFC 6585)
500 / 502 / 503 / 504Server-side failureBug / bad upstream / overloaded / upstream timeout
In everyday words
Pressing a lift button twice still brings one lift: that is idempotent. Ordering a pizza twice brings two pizzas: that is POST.
Actually
RFC 9110 §9.2.1: "Of the request methods defined by this specification, the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe." §9.2.2: "PUT, DELETE, and safe request methods defined by this specification are idempotent." RFC 5789 says "PATCH is neither safe nor idempotent". Note: "idempotent" is about the effect on the server, not the response. A second DELETE may return 404, but the resource is still gone, so the effect is the same.

Clients and proxies may retry idempotent requests automatically. Design PUT and DELETE so a repeat is harmless, and protect POST with an idempotency key (section 07).

Sources: RFC 9110 §9.2; RFC 9110 §15 (status codes); RFC 5789 (PATCH); RFC 6585 §4 (429)

06Pagination and versioning

Return big lists in pages; change APIs without breaking clients core

A list endpoint must never return everything at once. It returns one page plus a way to get the next one. There are two common ways.

QuestionOffset: ?limit=20&offset=40Cursor: ?limit=20&starting_after=obj_123
How it finds the pageSkip N rows, take the next 20Start right after a known item (an indexed key)
Jump to page 50YesNo, only next / previous
Cost of deep pagesGrows with the offset: skipped rows are still readSame for every page (index seek)
New rows inserted while pagingItems shift: duplicates or skipsStable: no duplicates or skips
Good forSmall admin tables with page numbersFeeds, timelines, big or fast-changing lists
Cursor pagination as SQL (keyset)
-- page 1
SELECT * FROM orders WHERE user_id = 42 ORDER BY id DESC LIMIT 20;
-- next page: continue after the last id you saw (here 9871)
SELECT * FROM orders WHERE user_id = 42 AND id < 9871 ORDER BY id DESC LIMIT 20;

Versioning

WayExampleTrade-off
URL path/v1/ordersVery clear and easy to route; changes every URL on a new major version
HeaderStripe-Version: 2026-09-30.endiveClean URLs, fine-grained dates; harder to test in a browser
Query parameter?api-version=2024-01-01Easy to try; easy to forget

Whatever the scheme, only add things in a version (new optional fields, new endpoints). Removing or renaming a field is a breaking change and needs a new version.

In everyday words
Offset is "start reading at page 300" in a book where pages keep being inserted. Cursor is a bookmark tucked after a sentence: you always continue at the right spot.
Actually
PostgreSQL's docs say: "The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient." Stripe's list APIs use cursors: starting_after and ending_before take an object ID, limit is 1–100 (default 10), and the reply has has_more. For versioning, Stripe pins each account to a dated API version and lets you override it per request with the Stripe-Version header; as of its docs page (checked 2026-10-25) it ships monthly versions without breaking changes and two major releases a year.

Default to cursor pagination and return an opaque next_cursor. Offsets are fine only for small tables where users need page numbers.

Sources: PostgreSQL · LIMIT and OFFSET; Stripe · Pagination; Stripe · Versioning (checked 2026-10-25)

07Idempotency keys for POST

How to retry a payment without charging twice deep dive interview favourite

Problem: the client sends "charge $10". The server does it, but the reply is lost on the network. The client cannot tell if the charge happened. If it retries a plain POST, the user may pay twice.

Fix: the client makes a unique idempotency key (like a random UUID) and sends it with the request. The server saves the result under that key. A retry with the same key gets the saved result, not a second charge.

Retrying a POST with an idempotency key The client posts with key K1; the server saves the reply under K1; the reply is lost; the client retries with K1 and gets the same saved reply instead of a second charge. POST, KEY=K1 SAVE REPLY 201, LOST RETRY, KEY=K1 FIND K1 SAVED REPLY SAME 201 Clientmakes K1 API servercharges once Key storekey → reply LEGEND Request Reply Retry is harmless
The retry hits the saved reply, so the card is charged once.
HTTP request
POST /v1/charges
Idempotency-Key: 8e1c2f0a-5b7d-4c1e-9a33-2f6b1d0c7e55
Content-Type: application/json

{"amount": 1000, "currency": "usd"}
In everyday words
A cloakroom ticket. If you hand in the same ticket twice, you get the same coat back, not a second coat.
Actually
Stripe's docs (API v1): keys are client-generated, "up to 255 characters long", V4 UUIDs are suggested; Stripe "saves the status code and body of the first request" with a key, and retries "return the saved response, including 500 errors". Keys may be pruned after they are "at least 24 hours old". The layer "compares incoming parameters to those of the original request and errors if they're not the same". If two requests with one key arrive at the same time, the second conflicts and is not saved. "All POST requests accept idempotency keys"; GET and DELETE do not need them. To build this yourself: store (key, user, request hash, status, response) with a unique constraint on the key, insert it in the same database transaction as the business change, and expire old keys.

Any money-moving or create-something POST should take an idempotency key. It turns "at least once" retries into "exactly once" effects.

Source: Stripe · Idempotent requests (checked 2026-10-25)

Other API styles

08gRPC and Protocol Buffers

Typed function calls between services core

gRPC lets one service call a function on another as if it were local. You describe the API in a .proto file. Tools then generate client and server code in many languages.

orders.proto
syntax = "proto3";

service Orders {
  rpc GetOrder (GetOrderRequest) returns (Order);            // unary
  rpc WatchOrders (WatchRequest) returns (stream Order);    // server streaming
}

message GetOrderRequest { int64 id = 1; }
message Order {
  int64  id     = 1;   // the number, not the name, goes on the wire
  string status = 2;
  int64  cents  = 3;
}
gRPC call typeShapeExample
Unary1 request → 1 responseGet order
Server streaming1 request → many responsesLive order updates
Client streamingmany requests → 1 responseUpload metrics in chunks
Bidirectional streamingboth sides streamChat, live sync
In everyday words
REST with JSON is sending a letter in plain English. gRPC is filling in a pre-printed form with numbered boxes: smaller and faster, but both sides need the same form.
Actually
gRPC's docs: "By default, gRPC uses Protocol Buffers" as both the IDL (interface language) and the message format, and defines the four call types above. Clients set deadlines; a call that runs out ends with DEADLINE_EXCEEDED. The gRPC wire protocol is "gRPC over HTTP2" (the official PROTOCOL-HTTP2.md). Protobuf docs: fields are identified by number; "field numbers in the range 1 through 15 take one byte to encode", and "reusing a field number makes decoding wire-format messages ambiguous", so you never reuse one. Browsers cannot speak raw gRPC, so web clients use gRPC-Web through a proxy such as Envoy.

Use gRPC for internal service-to-service calls (typed, compact, streaming). Keep REST/JSON at the public edge where browsers and partners call you.

Sources: grpc.io · Core concepts; grpc.io · Introduction; gRPC over HTTP2; protobuf.dev · proto3 guide; grpc.io · gRPC-Web basics

09GraphQL

The client asks for exactly the fields it needs core

GraphQL has one endpoint and a typed schema. The client sends a query listing the exact fields it wants, even across related objects. The server returns that shape and nothing more.

One request instead of three REST calls
query {
  user(id: 42) {
    name
    orders(first: 3) { id total }
    friends(first: 5) { name }
  }
}
QuestionRESTGraphQL
Over-fetching (too many fields)CommonAvoided: client picks fields
Under-fetching (many round trips)Common for screens with nested dataOne query
HTTP caching (CDN, browser)Easy: GET by URLHarder: usually POST to one URL
Server cost controlFixed per endpointNeeds depth / complexity limits
N+1 database queriesVisible per endpointEasy to hit in resolvers; batch them (DataLoader)
In everyday words
REST is a set menu: you get the whole plate. GraphQL is ordering à la carte: you list exactly what you want on one ticket.
Actually
The GraphQL spec defines three operation types: query ("a read-only fetch"), mutation ("a write followed by a fetch") and subscription ("a long-lived request that fetches data in response to source events"). The spec does not require HTTP; it only notes values are "often serialized when sent over a transport like HTTP". The N+1 problem: a resolver for friends runs once per user, causing one DB query per item; Facebook's dataloader library batches those into one.

GraphQL shines for many client screens over many back-end services. Pay for it with caching and query-cost controls.

Sources: GraphQL spec (October 2021); github.com/graphql/dataloader

10Real-time updates: polling, long polling, SSE, WebSockets

How the server tells the browser "something changed" interview favourite

HTTP is request-response: the server cannot speak first. Four patterns work around that.

  • Short polling: ask every N seconds. Simple, but most answers are "nothing new".
  • Long polling: ask, and the server holds the request open until there is news or a timeout.
  • SSE (server-sent events): one long HTTP response that streams events, server to client only.
  • WebSocket: upgrade the connection to a two-way pipe that stays open.
Long polling The browser sends a request; the server holds it until a new message arrives, replies, and the browser immediately asks again; on timeout the server replies empty and the browser asks again. holds open holds open GET /EVENTS NEW MESSAGE GET /EVENTS TIMEOUT, 204 GET /EVENTS Browserasks again Serverwaits for news LEGEND Request Empty reply News, sent at once
Long polling: news arrives as soon as it exists, at the cost of one held request per user.
OptionDirectionDelayPick it whenWatch out for
Short pollingClient asksUp to the poll intervalUpdates are rare and a few seconds late is fineWasted requests at scale
Long pollingClient asks, server waitsNear real-timeFallback where SSE/WebSockets are blockedOne held request per user; reconnect gaps
SSEServer → clientReal-timeFeeds, notifications, live scores, LLM token streamsOne-way only; 6-connection limit per browser and domain on HTTP/1.1
WebSocketBoth waysReal-timeChat, multiplayer games, collaborative editingStateful connections: harder load balancing, need your own reconnect and heartbeat logic
In everyday words
Short polling is asking "are we there yet?" every minute. Long polling is asking once and waiting for an answer. SSE is a radio broadcast. A WebSocket is a phone call.
Actually
SSE is in the WHATWG HTML standard: the response type is text/event-stream; the browser reconnects automatically and sends a Last-Event-ID header so the server can resume; the server can set the reconnect delay with a retry field. MDN warns that without HTTP/2, SSE is limited to "6" open connections per browser and domain. WebSocket (RFC 6455) starts as an HTTP/1.1 request that the server answers with 101 Switching Protocols, then becomes a "two-way communication" channel where "each side can, independently from the other, send data at will".

Default to SSE when only the server pushes; reach for WebSockets only when the client also streams. Either way, a fleet of stateful connections needs a pub/sub layer behind it (page 09).

Sources: WHATWG HTML · Server-sent events; MDN · EventSource; RFC 6455 (WebSocket)

Practice

—When to use what

Situation → pick → why decision table

SituationPickWhy
Public API for partners and browsersREST + JSON over HTTPSUniversal tooling, HTTP caching, easy to debug
Internal calls between your own servicesgRPC + protobufTyped contracts, compact binary, streaming, deadlines
Mobile/web app with many screens over many servicesGraphQL (often as a gateway)One round trip, client picks fields
Feed or table with millions of rowsCursor paginationConstant cost per page, no skips or duplicates
Payments, orders, any create that must not doublePOST + idempotency keySafe retries after timeouts
Server pushes notifications onlySSEPlain HTTP, auto-reconnect with Last-Event-ID
Chat, games, co-editingWebSocketTwo-way, low overhead per message
Rare updates, simplest possibleShort pollingNo held connections; fine if a delay is OK
Live video or voiceUDP-based (WebRTC, QUIC)A late packet is worse than a lost one
Mobile users on bad networksHTTP/3No TCP head-of-line blocking, survives network switches
Moving a site to a new hostLower DNS TTL a day beforeCaches expire quickly at switch time

—Common mistakes

Traps interviewers see every day traps

Trap
Retrying POST blindly
A timeout does not mean it failed. Without a key you may charge twice.
Trap
"HTTP/2 fixed head-of-line blocking"
Only at the HTTP layer. TCP still blocks all streams on a lost packet.
Trap
Offset paging on a big feed
Deep pages get slow and new posts cause duplicates. Use a cursor.
Trap
Verbs in URLs
/getUser, /deleteOrder. Use nouns plus HTTP methods.
Trap
200 for errors
Returning 200 {"error":...} breaks retries, caching and monitoring.
Trap
WebSockets for everything
If only the server pushes, SSE is simpler and works with normal HTTP tooling.
Trap
CNAME at the zone apex
Not allowed by RFC 1034. Use your provider's ALIAS/flattening.
Trap
Reusing a protobuf field number
Old clients decode garbage. Mark removed numbers reserved.

—Interview questions

18 questions, easy to hard, with model answers practice

What happens when you type a URL and press Enter? easy
Show answer

1) The browser parses the URL and checks its caches. 2) DNS: browser/OS cache, then a recursive resolver, which (if not cached) asks root → TLD → authoritative servers and returns an IP with a TTL. 3) TCP three-way handshake (SYN, SYN-ACK, ACK) to port 443. 4) TLS 1.3 handshake, one round trip, which also checks the server's certificate. 5) HTTP request; the server (often behind a CDN and load balancer) replies. 6) The browser parses HTML and fetches CSS, JS and images, reusing the connection.

Number: a fresh HTTPS connection over TCP + TLS 1.3 costs 3 round trips before the first byte, so about 450 ms on a 150 ms ocean link. Follow-up: "How would you make it faster?" → CDN, connection reuse, HTTP/3, DNS prefetch.

TCP vs UDP: differences and when to use each? easy
Show answer

TCP is connection-based and gives a reliable, ordered byte stream with flow and congestion control. UDP sends independent datagrams with no handshake, no ordering and no resend; its header is only 8 bytes vs TCP's 20+.

Use TCP for web, APIs, databases, file transfer: anything where every byte matters. Use UDP for DNS lookups, live voice and video, games: anything where late data is useless, or where the app builds its own reliability on top, like QUIC does for HTTP/3.

Which HTTP methods are idempotent, and why does it matter? easy
Show answer

Per RFC 9110: GET, HEAD, OPTIONS, TRACE (the safe ones) plus PUT and DELETE are idempotent. POST is not; PATCH is not either (RFC 5789).

It matters because networks fail mid-request. If a request is idempotent, the client, a proxy or a load balancer can retry it after a timeout without changing the outcome. For POST we add an idempotency key to get the same guarantee.

Note: idempotent means the same effect, not the same response. A second DELETE can return 404, and that is fine.

What do 401, 403, 404, 409, 429 and 503 mean? easy
Show answer

401: not authenticated (no or bad credentials). 403: authenticated but not allowed. 404: resource does not exist (also used to hide existence from unauthorised users). 409: conflicts with current state, such as a version mismatch or duplicate. 429: too many requests, rate limited; send Retry-After (RFC 6585). 503: service temporarily unavailable or overloaded; also often with Retry-After.

Why it matters: clients decide whether to retry by code. 4xx means "fix your request, do not retry as-is" (except 429). 5xx and 429 mean "retry later with backoff".

What are A, AAAA and CNAME records, and what is TTL? easy
Show answer

A maps a name to an IPv4 address; AAAA to an IPv6 address (RFC 3596); CNAME makes a name an alias of another name, so the resolver follows it. TTL is how many seconds resolvers may cache the answer.

Trade-off: a short TTL (say 60 s) lets you fail over or migrate quickly but means more DNS queries; a long TTL (hours) means fewer lookups but slow changes. A CNAME cannot sit at the zone apex because RFC 1034 forbids other data next to a CNAME, and the apex must hold SOA and NS records.

HTTP/1.1 vs HTTP/2 vs HTTP/3: what changed and why? medium
Show answer

HTTP/1.1 is text and handles one request at a time per connection, so browsers open several connections. HTTP/2 (RFC 9113) uses binary frames and multiplexes many streams on one TCP connection, with HPACK header compression. But one lost TCP packet still stalls every stream, as RFC 9113 itself admits.

HTTP/3 (RFC 9114) runs over QUIC on UDP. QUIC orders data per stream, so a loss only delays its own stream; it merges the transport and TLS 1.3 handshakes (1 RTT, 0-RTT on resume); and connection IDs let a connection survive switching from Wi-Fi to 4G. The HTTP semantics (RFC 9110) are the same in all three.

Offset vs cursor pagination: which would you use for an Instagram-style feed? medium
Show answer

Cursor. The feed is huge and new posts arrive constantly. With offset, page 2 shifts when new posts land at the top, so users see duplicates or miss items, and deep offsets get slow because the database still reads all skipped rows (PostgreSQL's docs say so).

With a cursor I return an opaque next_cursor that encodes the last item's sort key, e.g. (created_at, id), and query WHERE (created_at, id) < cursor ORDER BY ... LIMIT 20 on an index. Every page costs the same. Trade-off: no jumping to page 50, which a feed never needs.

How would you version a public REST API? medium
Show answer

Make additive changes without a version bump (new optional fields, new endpoints) and require clients to ignore unknown fields. Breaking changes (removing or renaming a field, changing types or meanings) get a new version.

Common options: version in the path (/v2/), simple and easy to route; or in a header, like Stripe's dated Stripe-Version, where each account is pinned to the version it started on and can upgrade per request. I would publish a deprecation timeline, log which clients still call old versions, and translate old requests at the edge so the core runs one model.

Design an idempotency layer for a payments POST endpoint. medium
Show answer

Client sends Idempotency-Key (a UUID). Server: 1) insert (key, account, request_hash, status=in_progress) with a unique constraint; if it already exists and is done, return the stored response; if in progress, return 409; if the hash differs, return an error (Stripe does this). 2) Do the charge and store the response in the same DB transaction as the business write. 3) Expire keys after, say, 24 hours (Stripe prunes keys at least 24 hours old).

Edge cases: crash between charge and save (do both in one transaction, or make the downstream call idempotent too with the same key), and scoping keys per account so users cannot collide.

REST vs gRPC vs GraphQL: when would you pick each? medium
Show answer

REST for public APIs: every language and tool speaks it, and GET responses cache well in CDNs and browsers. gRPC for internal service-to-service calls: protobuf contracts generate typed clients, binary messages are small, it supports streaming and deadlines, and it runs on HTTP/2. But browsers need gRPC-Web via a proxy. GraphQL for client-driven apps that combine data from many services: one round trip, exact fields; the costs are harder caching, N+1 resolver queries and the need for query-cost limits.

A common real setup: GraphQL or REST at the edge, gRPC inside.

Short polling, long polling, SSE, WebSockets: compare them. medium
Show answer

Short polling: client asks every N seconds; simple but wasteful and up to N seconds late. Long polling: the server holds the request until there is data or a timeout; near real-time, works anywhere, but reconnect gaps and one held request per user. SSE: one streaming HTTP response, server to client only, built-in reconnect with Last-Event-ID; limited to 6 connections per browser and domain on HTTP/1.1 (MDN). WebSocket: full-duplex after an HTTP 101 upgrade; best for chat and games but stateful, so load balancing and reconnect logic are on you.

Rule: server-push only → SSE; both directions → WebSocket.

Why is TLS 1.3 faster than TLS 1.2, and what is the risk of 0-RTT? medium
Show answer

TLS 1.2's full handshake takes two round trips. TLS 1.3 sends the client's key share in the first message, so the full handshake takes one round trip (RFC 8446 §2). On a 150 ms link that saves 150 ms per new connection.

0-RTT lets a returning client send data in its very first flight using a saved key. The risk is replay: an attacker can resend that early data, and the server may process it twice. So only idempotent requests like GET should be allowed in 0-RTT, never "transfer money".

A mobile client's request times out. Should it retry? How? hard
Show answer

Only if the operation is idempotent, or made idempotent with a key. A timeout means "unknown outcome": the server may have done the work.

How: exponential backoff with jitter (random spread, so a million phones do not retry at the same instant), a cap on attempts, respect Retry-After on 429/503, and a total deadline. Retry 5xx and network errors; do not retry most 4xx. Server side: rate limits and load shedding so retries cannot cause a retry storm. Number to quote: without jitter, a short outage can synchronise every client into waves of traffic at the same second.

What breaks if you put WebSocket servers behind a normal round-robin load balancer and scale to 20 nodes? hard
Show answer

Connections are long-lived and stateful. Problems: 1) A message for user B may arrive at node 3 while B is connected to node 17, so you need a pub/sub layer (Redis pub/sub, Kafka) or a "user → node" registry to route it. 2) Load becomes uneven: new nodes get no old connections, so rebalancing needs draining. 3) Deploys and crashes drop thousands of connections at once, causing a reconnect storm; add jittered reconnects. 4) The LB must allow long idle times and the HTTP upgrade, and use heartbeats so dead connections are noticed.

Follow-up: "How many connections per node?" → depends on memory per connection; measure, do not guess.

You changed a DNS record from the old IP to a new one, but some users still hit the old server hours later. Why? hard
Show answer

Caching at many layers: the browser, the OS, the recursive resolver, and any middle resolvers each keep the old answer until its TTL expires. If the old TTL was 24 hours, some resolvers can hold it for up to a day. Some resolvers and apps also ignore or clamp TTLs, and long-lived connections (keep-alive, HTTP/2, pooled DB connections) keep talking to the old IP without a new lookup at all.

Fix: lower the TTL well before the change (at least one old TTL ahead), keep the old server running and proxying for a while, and drain long-lived connections. For instant failover, use an IP that does not change (anycast or a load balancer) instead of DNS.

GraphQL query is slow: one query for 50 users and their orders hits the database 51 times. Explain and fix. hard
Show answer

This is the N+1 problem: one query for the users, then the orders resolver runs once per user, one query each. GraphQL resolvers are per field, so this happens naturally.

Fix: batch per request with a DataLoader: collect all 50 user IDs during one tick and run SELECT * FROM orders WHERE user_id IN (...) once, then hand each user its orders. That is 2 queries instead of 51. Also add per-request caching, query depth and complexity limits so a client cannot ask for friends-of-friends-of-friends, and persisted queries to allow CDN caching.

Design the API for a "like" button used by 100M users. hard
Show answer

Use PUT /posts/{id}/likes/{userId} to like and DELETE the same URL to unlike. Both are idempotent, so double taps and retries are harmless, with no idempotency key needed. Return 204. Read the count via GET /posts/{id} with like_count, served from a cache.

Scale: likes are write-heavy for viral posts, so store the (post, user) pair as the source of truth and update counters asynchronously through a queue, possibly sharded counters for hot posts. Clients can show the like immediately (optimistic UI). Return 429 with Retry-After if a client floods. Follow-up: "Exact counts?" → eventually consistent counts are fine for likes.

Design a live sports score service pushing updates to 5M concurrent viewers. hard
Show answer

Data only flows server → client, so I pick SSE (or WebSocket if the app already has one). Score events go from the ingest service into a pub/sub topic per match. A fleet of stateless-ish "edge push" servers subscribe to topics and fan out to their connected viewers; clients connect via a load balancer and reconnect with Last-Event-ID so they do not miss scores.

Sizing: at an assumed 50k connections per server, 5M viewers need about 100 servers plus headroom. Updates are tiny and rare (a few per minute), so bandwidth is small; the hard part is connection count and reconnect storms after deploys (drain gradually, jittered reconnect). Fallback for old clients: polling a CDN-cached JSON every few seconds, which is cheap because every viewer gets the same answer.

—Cheat sheet

Facts to memorise memorise

FactValueSource
DNS lookup chaincache → resolver → root → TLD → authoritativeRFC 1034
Root name servers13 names, 12 operatorsroot-servers.org
TCP handshakeSYN, SYN-ACK, ACK = 1 RTTRFC 9293
TLS 1.3 / TLS 1.2 handshake1 RTT / 2 RTTRFC 8446 / RFC 5246
QUIC handshake1 RTT (transport + TLS), 0-RTT on resumeRFC 9000, 9001
Header sizesUDP 8 B, TCP 20 B minimumRFC 768, 9293
HTTP RFCsSemantics 9110, HTTP/1.1 9112, HTTP/2 9113, HTTP/3 9114rfc-editor.org
Safe methodsGET, HEAD, OPTIONS, TRACERFC 9110 §9.2.1
Idempotent methodssafe ones + PUT, DELETERFC 9110 §9.2.2
Not idempotentPOST, PATCHRFC 9110, RFC 5789
Rate limited429 + Retry-AfterRFC 6585
Stripe idempotency key≤ 255 chars, pruned after ≥ 24 hStripe docs
Stripe list limit1–100, default 10Stripe docs
SSE limit without HTTP/26 per browser + domainMDN
WebSocket upgradeHTTP 101 Switching ProtocolsRFC 6455
Protobuf field numbers 1–151 byte to encodeprotobuf.dev

—Sources

All checked 2026-10-25 sources

WhatWhere
DNS concepts, CNAME ruleRFC 1034
DNS records, TTL formatRFC 1035; AAAA: RFC 3596
Root serversroot-servers.org
TCP, three-way handshakeRFC 9293
UDPRFC 768
TLS 1.3 handshake, 0-RTTRFC 8446 (replaced by RFC 9846, July 2026, same TLS 1.3 handshake); TLS 1.2: RFC 5246
QUIC, connection migration, HOLRFC 9000; RFC 9001
HTTP semantics, methods, status codesRFC 9110
HTTP/1.1, HTTP/2, HTTP/3RFC 9112; RFC 9113; RFC 9114
Header compressionRFC 7541 (HPACK); RFC 9204 (QPACK)
PATCH, 429RFC 5789; RFC 6585
Idempotency keysStripe · Idempotent requests
Cursor pagination, versioningStripe · Pagination; Stripe · Versioning
OFFSET costPostgreSQL · LIMIT and OFFSET
gRPCgrpc.io core concepts; gRPC over HTTP2; gRPC-Web
Protocol Buffersprotobuf.dev proto3 guide
GraphQLGraphQL spec; DataLoader
SSEWHATWG HTML; MDN EventSource
WebSocketRFC 6455