TOPIC 09

Communication Protocols

How machines actually talk: the transport layer beneath everything, the evolution of HTTP, and the API styles — REST, gRPC, WebSockets — you'll choose between in every design.

Intermediate 30 min read 10 concepts

At a Glance

TCP vs UDP: The Foundation

Almost everything rides on one of two transport protocols, and their personalities couldn't differ more.

TCP is the careful courier: it establishes a connection with a three-way handshake, numbers every byte, acknowledges receipt, retransmits losses, and delivers everything in order. Reliability costs latency — at minimum one full round trip before the first byte of data.

Client Server 1 · SYN "can we talk?" → ← 2 · SYN-ACK "yes! you ready?" 3 · ACK "ready — let's go" → connection open — data flows both ways

The TCP three-way handshake: one round trip of politeness before any data moves.

UDP is the confetti cannon: no handshake, no acknowledgments, no ordering — just fire datagrams and hope. What you lose in reliability you gain in speed and per-packet independence, which is exactly right when fresh beats complete.

TCPUDP
ConnectionHandshake requiredConnectionless
DeliveryGuaranteed, ordered, error-checkedBest effort — drops and reordering happen
SpeedSlower (ACKs, congestion control)Minimal overhead
Use forWeb, APIs, email, file transfer, databasesVideo/voice calls, game state, DNS lookups, live streams

The intuition: a missing chunk of a bank transfer is a catastrophe (TCP retransmits it); a missing 40 ms of your voice call is a blip that retransmission would only make worse by delivering it late (UDP shrugs and moves on).

TCP vs UDP — When Precision vs Speed Wins

Let's make the choice concrete. TCP gives you the full reliability stack: a 3-way handshake (SYN → SYN-ACK → ACK), sequence numbers on every byte, acknowledgments, retransmission of losses, flow control (don't drown the receiver), and congestion control (don't drown the network). The guarantees: ordered, reliable, no duplicates. UDP gives you none of that — no handshake, no ACKs, no ordering. It just sends packets. The reward is speed and minimal overhead.

TCP: handshake first Client Server SYN SYN-ACK ACK data (finally) UDP: just send Client Server data data data (lost — nobody retries) data

TCP pays a round trip of politeness for guarantees; UDP starts shouting immediately and never looks back.

HTTP: Three Generations

HTTP is the request/response protocol of the web: a client sends a method + path + headers (+ body), the server returns a status code + headers + body. Methods map to intent — GET (read), POST (create), PUT (replace), PATCH (modify), DELETE (remove). Status codes tell the story: 2xx success, 3xx redirect, 4xx your fault, 5xx our fault.

1
HTTP/1.1 (1997)

One request at a time per TCP connection. Pipelining existed but responses had to return in order — one slow response blocks everything behind it (head-of-line blocking). Browsers coped by opening ~6 parallel connections per host; developers coped with sprite sheets and bundling.

2
HTTP/2 (2015)

Multiplexing: many requests and responses interleave as binary frames on one connection — no more waiting in line at the HTTP layer. Plus header compression (HPACK) and server push. One gotcha remains: a single lost TCP packet still stalls all streams, because TCP itself delivers in order.

3
HTTP/3 (2022)

Solves that by abandoning TCP: it runs on QUIC, built atop UDP. Streams are independent (one lost packet stalls only its own stream), the handshake merges transport + TLS into 0–1 round trips, and connections survive switching from Wi-Fi to cellular. Google and Cloudflare serve a huge share of traffic this way already.

HTTP Deep Dive — From 1.0 to 3.0

The three-generation summary above is the interview soundbite; here's the engineering detail behind each jump:

HTTP/1.1 — sequential req 1: index.html req 2: app.css (waits for 1) req 3: app.js (waits for 1 and 2) total time = sum of all three responses HTTP/2 — multiplexed on one connection stream 1: index.html stream 3: app.css stream 5: app.js all three in flight simultaneously — total time ≈ the slowest single response

HTTP/1.1 queues requests behind each other; HTTP/2 interleaves them as frames on one TCP connection.

REST: The Web's Lingua Franca

REST models your API as resources (nouns, not verbs) manipulated through uniform HTTP methods:

GET /users/42 # read user 42
POST /users # create a user
PUT /users/42 # replace user 42
PATCH /users/42 # update part of user 42
DELETE /users/42 # remove user 42

REST Best Practices — Building APIs That Last

Knowing REST's principles is easy; designing an API your consumers won't curse in two years is the hard part. The checklist:

Status Codes Matter

ClassCodes you should actually use
2xx success200 OK · 201 Created (with a Location header) · 204 No Content (successful DELETE)
4xx client error400 Bad Request · 401 Unauthorized (who are you?) · 403 Forbidden (I know, and no) · 404 Not Found · 409 Conflict · 422 Unprocessable Entity · 429 Too Many Requests
5xx server error500 Internal Server Error · 503 Service Unavailable (with Retry-After)

RPC & gRPC: Just Call the Function

RPC frames network calls as function calls: userService.getUser(42) instead of building an HTTP request. gRPC (Google's open-source RPC) is the modern standard: you define services and messages in Protocol Buffers, and the toolchain generates typed client/server code in any language. It rides HTTP/2, encodes messages in compact binary, and supports four shapes: unary, server-streaming, client-streaming, and bidirectional streaming.

REST + JSONgRPC + Protobuf
PayloadText JSON — human-readableBinary — smaller, faster to parse
ContractOptional (OpenAPI)Mandatory .proto — compile-time types
StreamingAwkwardFirst-class, bidirectional
Browser supportUniversalNeeds gRPC-Web proxy
Debuggabilitycurl and read itNeeds tooling
Sweet spotPublic APIs, web/mobile clientsInternal service-to-service at scale

The common pattern at Netflix, Google, and Uber: REST (or GraphQL) at the edge for clients, gRPC inside between microservices.

gRPC — The Microservices Protocol

Going one level deeper than the comparison table: gRPC is Google's RPC framework, using Protocol Buffers (protobuf) both as the interface definition language (IDL) and the binary wire format, running over HTTP/2. Why it's fast: protobuf binary encoding is 5–10× smaller than equivalent JSON, HTTP/2 multiplexes calls over one connection, and streaming is built in rather than bolted on.

You define the contract once in a .proto file:

syntax = "proto3";

service UserService {
 rpc GetUser(GetUserRequest) returns (User);
 rpc ListUsers(ListUsersRequest) returns (stream User);
 rpc UpdateUser(stream UpdateUserRequest) returns (User);
}

message User {
 string id = 1;
 string name = 2;
 string email = 3;
}

From this, the toolchain generates client stubs and server interfaces in Go, Java, Python, and a dozen other languages — type-safe at compile time, identical semantics everywhere.

1:1

Unary

One request, one response. The classic function call — most APIs are this.

1:N

Server streaming

One request, a stream of responses. "Subscribe to price updates for AAPL."

N:1

Client streaming

A stream of requests, one response. "Upload these sensor readings, tell me when done."

N:N

Bidirectional

Both sides stream independently. Chat, live sync, telemetry with control messages.

When gRPC: internal microservices, high-throughput APIs, streaming use cases. When REST: public APIs (easier to consume and explore), browser-facing endpoints (gRPC needs a grpc-web proxy in browsers), and whenever flexibility matters more than raw performance.

Real-Time: Polling, SSE, and WebSockets

HTTP is client-initiated — but chat apps, dashboards, and trading screens need the server to speak first. Three escalating options:

Long pollingServer-Sent Events (SSE)WebSockets
How it worksClient asks; server holds the request open until news arrives; repeatOne HTTP response that never ends; server streams events down itHTTP connection upgrades to a persistent, full-duplex socket
DirectionServer → client (per request)Server → client onlyBoth ways, anytime
OverheadHigh — repeated requests & headersLowLowest after setup (~2-byte frames)
ReconnectionInherent (it's just requests)Automatic, with replay via Last-Event-IDManual (you write it)
Best forLegacy fallback, rare updatesFeeds, notifications, AI token streamsChat, games, collaborative editing, trading

WebSockets power Slack and Discord conversations and every serious multiplayer or trading UI. SSE is having a renaissance: it's exactly how ChatGPT-style apps stream tokens. Long polling survives as the universal fallback.

GraphQL, briefly

GraphQL lets clients query exactly the fields they need in one request — solving REST's over-fetching and the mobile "n round trips" problem. Costs: server complexity, tricky caching (everything's a POST), and the risk of pathological queries. Facebook built it; GitHub's public API v4 showcases it.

Archie says

When an interviewer says "real-time," interrogate the requirement before saying WebSockets. One-way updates every few seconds? SSE is simpler and proxies love it. True two-way interactivity at sub-100ms? Now you've earned the socket.

WebSockets vs SSE vs Long Polling — The Mechanics

The comparison table above tells you which; here's how each one actually works under the hood:

1
Long polling

Client sends a request; the server holds it open until data is available (or ~30s passes), returns it, and the client immediately re-sends. It simulates push over plain HTTP — but wastefully: one TCP connection per in-flight request, full headers every cycle. Today it's a fallback, not a first choice.

2
SSE (Server-Sent Events)

Client opens one HTTP connection; the server streams events down it whenever it wants. One-directional (server → client only), auto-reconnect built into the browser API, works over standard HTTP/1.1 — so every proxy, LB, and CDN understands it.

3
WebSockets

Full-duplex: client and server can send at any time. Starts as an HTTP request that upgrades (101 Switching Protocols) — after that it's not HTTP anymore, so it only passes through LBs and proxies that understand WS. You own reconnection logic and connection state.

Real-world receipts

Discord uses WebSockets for everything real-time — messages, presence, voice signaling — holding roughly 5M concurrent WS connections at peak. GitHub uses SSE for live PR and issue updates on the page: cheaper than WS and entirely sufficient for one-way updates.

GraphQL — Query What You Need

REST gives fixed responses, which creates two mirror-image problems. Over-fetching: a mobile client needs 5 fields from a 50-field response but downloads all 50. Under-fetching: the screen needs data from 3 endpoints, so it makes 3 round trips. GraphQL fixes both: the client specifies exactly which fields it needs, and gets one response with exactly that.

The server defines a typed schema:

type User {
 id: ID!
 name: String!
 posts: [Post!]!
}
type Query {
 user(id: ID!): User
}

And the client asks for precisely what it wants:

query {
 user(id: "123") {
 name
 posts { title createdAt }
 }
}
!
The N+1 problem

Naively resolved, "user with posts, each with its author" queries the DB once for the user, once for the posts, then once per post for each author — 1 + N queries. The standard fix is DataLoader: it batches all the author lookups in a tick into one WHERE id IN (...) query and caches results per request.

When GraphQL: complex APIs with many entity types, mobile clients needing custom response shapes, and rapid frontend iteration without backend changes. When REST: simple CRUD, public APIs, and anywhere HTTP caching matters — GraphQL's everything-is-a-POST makes caching much harder.

API Gateway

An API Gateway is a managed reverse proxy that sits between clients and your backend services, acting as the single entry point for all external requests. Instead of clients knowing the address of every microservice, they call the gateway — which handles routing, authentication, rate limiting, caching, and protocol translation on their behalf.

Core responsibilities:

API Gateway vs. Load Balancer: A load balancer distributes traffic across identical instances of the same service (Layer 4 or Layer 7 routing). An API Gateway operates at Layer 7 and understands application-level semantics — it routes by path, method, and headers; enforces business policies; and can call multiple backends and merge their responses. Many architectures use both: the gateway as the intelligent front door and a load balancer behind it distributing traffic to each service's pool of instances.

API Gateway vs. Service Mesh: An API Gateway manages north-south traffic (external clients ↔ backend). A service mesh (Istio, Linkerd) manages east-west traffic (service ↔ service inside the cluster) — handling mutual TLS, retries, circuit breakers, and observability between microservices. Production systems often run both together.

How rate limiting works inside a gateway: On each request the gateway increments a counter in a distributed store (Redis) keyed by client ID and time window. If the counter exceeds the limit it returns 429 Too Many Requests with a Retry-After header. The token bucket algorithm allows short bursts above the average rate; the sliding window log is more precise but memory-intensive. All gateway instances share the same Redis counter so the limit is globally enforced across the fleet.

Failure modes and resilience: The gateway is a potential single point of failure — run multiple instances behind a load balancer. If the gateway itself is overloaded, back-pressure cascades to clients. Use circuit breakers so the gateway stops forwarding to a failing backend and returns a fast error rather than queueing requests indefinitely. Health checks on backend routes let the gateway stop routing to unhealthy pods automatically.

Real-world API gateway products: AWS API Gateway (managed, pay-per-call), Kong (open-source, Nginx-based, plugin ecosystem), Apigee (Google Cloud, enterprise), NGINX (self-managed reverse proxy + gateway features), Envoy (programmatic, used by Istio), and Azure API Management. Choosing between them is mostly a build-vs-buy and cloud-vendor decision rather than a technical one — the patterns above apply to all of them.

Interview Q&A

Three protocol questions interviewers actually ask — click to reveal strong answers.

WebSockets, for bidirectional real-time updates: each keystroke and cursor move must reach other users in <100ms, and SSE's one-directional nature doesn't work because clients also send changes. Use Operational Transform (OT) or CRDTs to merge concurrent edits without conflicts. Architecture: (1) Client sends operations over WS to the server. (2) Server applies the operation to document state, resolves conflicts via OT/CRDT, and broadcasts to all other WS connections for this document. (3) Server persists operations asynchronously to the DB. (4) With multiple servers, add a pub/sub layer (Redis pub/sub) so changes reach clients connected to different server instances.

Choose gRPC when: (1) High throughput — thousands of calls/sec benefit from binary encoding and HTTP/2 multiplexing. (2) Streaming — gRPC bidirectional streaming is natural; REST polling is awkward. (3) Strict typing — generated clients catch API contract violations at compile time, not runtime. (4) Polyglot services — gRPC code-gen works identically in Go, Java, Python. Choose REST when: browser clients are involved (gRPC-web is clunky), it's a public API (REST is easier to explore and test), caching is critical (HTTP caching works naturally with REST GET), or the team is unfamiliar with protobuf.

Token bucket in Redis. Per-client limit: 100 req/minute. On each request: (1) INCR rate:{client_id}:{minute_bucket}; if > 100, return 429 with a Retry-After header. (2) If it's a new key, SET EX 60. Use Redis pipelining to make both operations atomic. Distribution: all API servers hit the same Redis cluster, so the limit is globally enforced. Handle Redis failure deliberately: fail open (allow requests) or fail closed (reject) — most choose fail-open with alerts. For burst precision: sliding window log (more precise, more memory) or sliding window counter (the compromise). Return X-RateLimit-* headers on every response, and differentiate tiers — free at 100/min, paid at 10K/min — with the tier stored in Redis alongside the counter.

Anti-Patterns to Avoid

WebSockets for infrequent server-push

"We need to notify users of events" does not mean WebSockets. SSE is simpler, cheaper, works over plain HTTP, and auto-reconnects. Only use WS when clients also send data.

Exposing internal gRPC directly to browsers

Browsers can't speak raw gRPC (the HTTP/2 trailers issue). You'd need a grpc-web proxy (Envoy or middleware). Simpler: REST or GraphQL for browser-facing APIs, gRPC internally.

REST API without versioning

Adding a required field to a response breaks clients that don't expect it. Always version public APIs. Never break existing versions — deprecate with sunset dates.

Interview Prep

Communication Protocols Interview Questions

All key questions and answers from this topic that appear in system design interviews.

HTTP Questions

HTTP (HyperText Transfer Protocol) is the foundation of communication on the web. It enables the transfer of resources such as web pages, images, and API responses.

How it works: A client (browser, mobile app, API client) sends an HTTP request to a web server. The server processes the request and returns an HTTP response containing data or an error message. The client receives the response and renders the requested resource.

Example: When you type a URL in a browser, the browser sends a GET request to the web server. The server responds with the HTML of the webpage. The browser displays it to the user.

HTTP does not retain memory of previous requests between the client and server. Each request is treated as independent — the server does not store session information.

Challenges: Maintaining user sessions (e.g., staying logged in), and every request must include necessary information (e.g., authentication tokens).

Solutions to maintain state: Cookies (stored in the browser and sent with requests), Sessions (server-side storage with a session ID), and Tokens like JWT and OAuth (used for authentication and API security).

Security: HTTP sends data in plain text; HTTPS encrypts data using SSL/TLS.

Encryption: HTTP has no encryption; HTTPS uses SSL/TLS encryption.

Port: HTTP uses port 80; HTTPS uses port 443.

Data Integrity: HTTP data can be intercepted and modified; HTTPS data is protected from tampering.

Use Case: HTTP is suitable for non-sensitive data; HTTPS is essential for secure websites (banking, login pages, any site handling user data).

The HTTP request-response cycle is the fundamental pattern by which clients and servers communicate:

Step 1 — Client sends a request: The browser or API client sends an HTTP request specifying a method (GET, POST, etc.), a URL, headers (user-agent, content-type, auth tokens), and optionally a body (for POST/PUT).

Step 2 — Server processes the request: The web server receives the request, authenticates/authorizes if needed, retrieves or computes the requested resource (querying a database, calling another service, reading a file, etc.).

Step 3 — Server sends a response: The server responds with a status code (e.g., 200 OK, 404 Not Found), response headers (content-type, cache-control, etc.), and a body containing the data (HTML, JSON, image, etc.).

Step 4 — Client renders the response: The browser parses and renders HTML, or the API client parses the JSON payload and updates the application state.

Example: A user visits a blog. The browser sends GET /articles/123 HTTP/1.1. The server queries the database, finds the article, and responds with 200 OK and the article HTML. The browser renders the page.

GET — Retrieve a resource. POST — Create a new resource. PUT — Update an entire resource (idempotent). PATCH — Partially update a resource. DELETE — Remove a resource.

PUT vs. PATCH: Use PUT when replacing an entire resource (e.g., updating a full user profile with all fields). Use PATCH when making partial updates (e.g., changing only the email address of a user without modifying the name or password).

1xx — Informational: Request received and processing continues.

2xx — Success: 200 OK (successful request), 201 Created (new resource created), 204 No Content (success with no response body).

3xx — Redirection: 301 Moved Permanently (resource has a new permanent URL, search engines update it), 302 Found (temporary redirect, search engines keep original URL), 304 Not Modified (use cached version).

4xx — Client Errors: 400 Bad Request (invalid syntax), 401 Unauthorized (authentication required), 403 Forbidden (lacks permission), 404 Not Found (resource doesn't exist).

5xx — Server Errors: 500 Internal Server Error (generic server failure), 503 Service Unavailable (server temporarily overloaded or down).

Cookies: Small pieces of data stored in the client's browser. Automatically sent with every request to the server. Used for tracking user sessions, preferences, and authentication.

Sessions: Server-side storage of user data. The client is assigned a session ID stored in a cookie. Common in login-based applications.

Tokens (JWT, OAuth): JSON Web Tokens (JWT) are used in stateless authentication. OAuth tokens provide secure API access. Tokens are stored in local storage or cookies and sent in request headers.

HTTP caching reduces server load and improves performance by allowing browsers to store copies of resources.

Cache-ControlCache-Control: max-age=3600 (cache for 1 hour), Cache-Control: no-cache (always fetch fresh data), Cache-Control: private (don't cache in shared caches).

ETag — A unique identifier for a resource version, used for conditional requests to check if content has changed.

Expires — Defines the expiration date/time for cached content (older mechanism, Cache-Control preferred).

Risks: Man-in-the-middle attacks (data interception), phishing attacks (fake websites), data tampering (modifying HTTP messages), session hijacking (stealing session cookies).

Mitigations: Use HTTPS to encrypt data with SSL/TLS. Use HttpOnly and Secure cookie flags. Implement Content Security Policy (CSP) to prevent XSS. Use JWT or OAuth for secure API access. Validate all user input to prevent injection attacks.

Both are 3xx redirect status codes that tell the client to look for the resource at a different URL, but they differ in permanence and caching behavior:

301 Moved Permanently — The resource has been permanently moved to a new URL. Browsers and search engines cache this redirect. Future requests go directly to the new URL without asking the original server. Search engine "link juice" is transferred to the new URL.

302 Found (Moved Temporarily) — The resource is temporarily at a different URL. Browsers do NOT cache this redirect by default. The original URL remains authoritative; future requests still go to the original URL first. Search engines do NOT transfer ranking to the temporary URL.

When to use which: Use 301 when permanently changing a URL (e.g., migrating a website, retiring an old endpoint). Use 302 for temporary redirects (e.g., A/B testing, maintenance pages, login redirects after authentication).

TCP & UDP Questions

TCP (Transmission Control Protocol) is connection-oriented. It establishes a reliable connection before sending data and ensures packets arrive in the correct order and without loss. Slower but reliable.

UDP (User Datagram Protocol) is connectionless. It sends data without establishing a connection and does not guarantee delivery, order, or error correction. Faster but unreliable.

Summary: TCP prioritizes reliability; UDP prioritizes speed.

Use TCP when data integrity is critical and transmission reliability is required:

Web Browsing (HTTP, HTTPS) — Ensures all webpage data is received without corruption.

File Transfers (FTP, SFTP) — Guarantees complete file downloads/uploads without missing chunks.

Email (SMTP, IMAP, POP3) — Ensures emails are received in full without data loss.

Database Queries — Prevents corruption of data when sending/receiving requests and responses.

TCP is essential whenever missing or corrupted data cannot be tolerated.

UDP is preferred when speed is more important than reliability and occasional data loss is acceptable:

Video Streaming (YouTube, Netflix) — A few lost frames don't significantly affect the user experience.

Online Gaming (Multiplayer) — Low latency is crucial for real-time interactions.

VoIP Calls (Skype, Zoom) — Small packet losses are tolerable, but delays should be minimal.

DNS Lookups — Quick responses are needed, and lost packets can be retried without noticeable delay.

Three-Way Handshake — Establishes a connection (SYN → SYN-ACK → ACK) before data transmission.

Acknowledgments (ACKs) — Confirms receipt of packets; sender knows data arrived.

Retransmission of Lost Packets — If a packet is lost (no ACK received), TCP resends it.

Error Checking — Uses checksums to detect corrupted packets.

Ordered Data Delivery — Reassembles packets in the correct order before passing to the application.

UDP is faster because: no connection establishment (no handshake), no acknowledgments (data sent without waiting for confirmation), no retransmission (lost packets are not resent), and lower overhead (8-byte header vs. TCP's 20-byte header). Since UDP doesn't waste time ensuring reliability, it delivers data as quickly as possible.

Yes, some applications combine both protocols to optimize performance:

Online Video Streaming (Netflix, YouTube) — UDP for fast video delivery, TCP for control messages (pause, seek).

Online Games — UDP for real-time actions (player movement), TCP for non-time-sensitive data (chat, game stats).

VoIP (Skype, Zoom) — UDP for low-latency voice data, TCP for connection setup.

By using both protocols strategically, applications balance speed and reliability.

DNS primarily uses UDP because: speed is crucial (DNS queries must resolve quickly), and DNS requests/responses are small enough to fit within UDP's packet size limits. However, DNS can use TCP for larger responses, such as when retrieving a full DNS zone transfer.

Since UDP doesn't ensure reliable delivery, applications must implement their own mechanisms:

Sequence Numbers — Ensures packets arrive in the correct order at the application level.

Custom Acknowledgments — Applications can request ACKs for important data.

Forward Error Correction (FEC) — Adds extra data to allow recovery of lost packets without retransmission.

Application-Level Retransmission — If a critical packet is lost, the application requests it again.

TCP Disadvantages: Slower performance due to reliability mechanisms; higher resource consumption (CPU and memory); not suitable for real-time applications (e.g., VoIP, live streaming) where latency matters more than perfect delivery.

UDP Disadvantages: No guarantee of delivery (some packets may be lost); no built-in error correction (applications must handle errors manually); unordered delivery (packets may arrive out of sequence).

TCP traffic is easier to monitor since it has a clear connection setup (handshake) and termination — firewalls can track state reliably.

UDP traffic is harder to track because it's connectionless, making it more vulnerable to abuse (e.g., UDP-based DDoS attacks). To secure UDP traffic, firewalls use rate limiting, deep packet inspection, and UDP filtering.

The Three-Way Handshake is TCP's connection establishment process. It synchronizes both sides before any data is sent, ensuring both client and server are ready to communicate:

Step 1 — SYN (Synchronize): The client sends a SYN packet to the server to request a connection and share its initial sequence number.

Step 2 — SYN-ACK (Synchronize-Acknowledge): The server responds with a SYN-ACK, acknowledging the client's SYN and sharing its own sequence number.

Step 3 — ACK (Acknowledge): The client sends an ACK back to the server, confirming the connection is established. Data transfer begins.

Why it's needed: It ensures both sides agree on initial sequence numbers (for ordering packets), confirms both sides are reachable and ready, and establishes the connection state before data is sent. UDP skips this handshake entirely — which makes UDP faster but unreliable.

TCP uses congestion control algorithms to dynamically adjust its transmission rate based on network conditions, preventing it from overwhelming the network:

Slow Start — When a new connection is established, TCP starts with a small congestion window and doubles the number of segments sent per round-trip time until a threshold or packet loss is detected.

Congestion Avoidance — After the slow-start threshold is reached, TCP increases the window size linearly (one segment per RTT) to probe for more available bandwidth without causing congestion.

Fast Retransmit — If TCP receives 3 duplicate ACKs for the same packet, it infers a packet was lost and retransmits it immediately without waiting for a timeout.

Fast Recovery — After fast retransmit, TCP halves the congestion window (instead of resetting to 1) and resumes congestion avoidance to recover quickly.

UDP has no congestion control — it fires packets at a fixed rate regardless of network conditions, which is why UDP applications (games, streaming) must implement their own flow control if needed.

UDP is the standard choice for real-time multiplayer games, because low latency matters more than guaranteed delivery.

Why not TCP? TCP's retransmission and ordering guarantees introduce head-of-line blocking — if one packet is lost, TCP stalls all subsequent packets waiting for the retransmit. In a game, a stale position update is worse than a missed one because the game state has already moved on.

Why UDP works: If a player's position packet is dropped, the next position update (milliseconds later) makes the old one irrelevant anyway. Games discard late/out-of-order packets rather than waiting for retransmission.

What games add on top of UDP: Custom reliability layers for critical events (hits, kills, game state changes) that must not be lost. Sequence numbering to detect and discard out-of-order packets. Delta compression to minimize payload size. Examples: Fortnite, Call of Duty, Valorant all use UDP with custom reliability built on top.

It depends on the root cause of the buffering, but in many cases switching to UDP (or a UDP-based protocol) can reduce buffering:

Why TCP may cause buffering: TCP's retransmission of lost packets causes head-of-line blocking — the decoder stalls waiting for retransmitted packets even though the stream has moved past that point. TCP's congestion control can also throttle throughput aggressively when packet loss occurs.

Why UDP helps streaming: UDP skips retransmission. If a frame is lost, the decoder interpolates or drops it and keeps playing. This eliminates the stall-and-wait pattern. Modern streaming protocols like QUIC (used by YouTube, Google), WebRTC (video calls), and HLS over UDP-based transport exploit this.

Caveats: Simply switching to raw UDP is not enough — you still need a buffer, forward error correction (FEC), and adaptive bitrate logic. The real improvement comes from using adaptive streaming protocols (HLS, DASH) plus QUIC rather than raw UDP. If buffering is caused by low bandwidth (not protocol overhead), switching protocols won't help.

REST & RESTful API Design Questions

REST (Representational State Transfer) is an architectural style that defines constraints for designing web services using standard HTTP methods. SOAP (Simple Object Access Protocol) is a strict protocol.

Protocol: REST is an architectural style; SOAP is a strict protocol.

Data Format: REST uses JSON (or XML/HTML); SOAP uses XML only.

Performance: REST is lightweight and faster; SOAP has higher overhead due to XML formatting and WS-Security.

Statefulness: REST is stateless; SOAP can be stateful.

1. Client-Server Architecture — Client and server are independent, allowing better scalability.

2. Statelessness — Each request contains all necessary information; the server doesn't store session data.

3. Cacheability — Responses can be cached to improve performance and reduce server load.

4. Layered System — An API can be designed in layers (authentication, load balancing, security) without affecting clients.

5. Uniform Interface — Standardized communication using HTTP verbs (GET, POST, PUT, DELETE).

6. Code on Demand (Optional) — The server can send executable code (e.g., JavaScript) to the client.

REST API: Any API that follows some REST principles but may not strictly adhere to all six constraints.

RESTful API: An API that fully follows all REST constraints and principles — truly stateless, proper HTTP verbs, uniform interface, etc.

Use plural nouns for resource names (/users, not /user).

Implement proper HTTP status codes for all responses.

Support versioning (/v1/resources) to maintain backward compatibility.

Use pagination for large datasets (?page=2&limit=20).

Implement rate limiting to prevent API abuse.

Use OAuth 2.0 or JWT for authentication.

Use HTTPS for all endpoints.

HATEOAS (Hypermedia as the Engine of Application State) is a REST principle where API responses include links to relevant actions the client can take next. This makes the API self-discoverable.

Example: A response for a user includes links like "self": "/users/1" and "orders": "/users/1/orders", so clients can navigate without hardcoding URLs.

URI-based: /v1/resource — Most common and explicit approach.

Header-based: Accept-Version: v1 — Keeps URLs clean but less visible.

Query-based: ?version=1 — Easy to use but can conflict with other query params.

REST — JSON/XML, fixed endpoints, medium performance. Best for general APIs and CRUD operations.

GraphQL — JSON, custom queries (client specifies exactly what data it needs), high performance for complex data fetching. Best when clients need flexible data shapes.

gRPC — Protocol Buffers (binary), strict method definitions, very high performance and low latency. Best for internal microservice communication and low-latency services.

Use query parameters: GET /users?page=2&limit=10

Return pagination metadata in the response: total count, current page, next/previous links. Always paginate large collections to avoid performance issues and excessive payload sizes.

OAuth 2.0 — Used for user authentication and delegated access (e.g., "Login with Google").

JWT (JSON Web Token) — Used for stateless session management. Token contains encoded user info and is verified on each request without server-side session storage.

API Keys — Simple tokens for third-party access, typically used for server-to-server communication.

Enable caching using HTTP Cache-Control headers and CDNs. Use gzip/Brotli compression. Implement asynchronous processing for long-running tasks. Use database indexing and read replicas. Implement pagination to avoid large payloads. Use connection pooling. Consider moving to gRPC for internal high-frequency calls.

Prevent SQL injection by using parameterized queries (never interpolate user input into SQL). Use CSRF tokens to prevent cross-site request forgery. Implement HTTPS for all secure communication. Validate all input at API boundaries. Rate-limit endpoints to prevent brute force and DDoS. Use CORS policies to restrict cross-origin access.

A blogging platform has three main resources — users, posts, and comments — and the API should be structured around them:

Users: GET /users/{id} — Get user profile. POST /users — Register a new user. PUT /users/{id} — Update profile. DELETE /users/{id} — Delete account.

Posts: GET /posts — List all posts (with pagination: ?page=1&limit=20). GET /posts/{id} — Get a specific post. POST /posts — Create a new post (requires auth). PUT /posts/{id} — Update a post. DELETE /posts/{id} — Delete a post.

Comments (nested under posts): GET /posts/{id}/comments — Get all comments for a post. POST /posts/{id}/comments — Add a comment. DELETE /posts/{post_id}/comments/{comment_id} — Delete a comment.

Best practices applied: Use plural nouns for collections. Nest child resources under parents when tightly coupled. Use query params for filtering/sorting (GET /posts?author=alice&sort=created_at). Return appropriate status codes (201 Created, 204 No Content for DELETE). Version the API: /v1/posts. Authenticate with JWT/OAuth for write operations.

Rate Limiting caps the number of requests a client can make in a given time window (e.g., 1000 requests per hour per API key). Once the limit is exceeded, the server returns 429 Too Many Requests.

Throttling is a related concept that slows down rather than blocks requests when a threshold is reached — the server still processes them but deliberately delays responses.

Why they matter:

Protect availability — Prevent a single client (or bot) from consuming all server resources and degrading service for others. Essential protection against DDoS-style abuse.

Fair resource allocation — Ensures equitable access across thousands of API consumers (free vs. paid tiers can have different limits).

Cost control — APIs backed by expensive operations (ML inference, database queries) use rate limits to bound compute costs.

Implementation: Rate limits are typically tracked in Redis (fast in-memory counters per key). Common algorithms: Token Bucket (allows burst traffic up to a limit), Leaky Bucket (smooths out requests), and Fixed Window (simple per-time-period counter).

Backward compatibility means existing clients continue to work without changes after the API is updated. Key strategies:

API Versioning — Introduce a new version URL (/v2/users) when making breaking changes. Maintain the old version for a deprecation period so existing clients can migrate gradually.

Never remove or rename fields — Only add new optional fields to response bodies. Removing or renaming fields is a breaking change for clients that parse those fields.

Never change field types — If a field was a string, keep it a string. Type changes break client-side parsing.

Expand, don't contract — Add new endpoints alongside old ones rather than replacing them. Keep old endpoints functional until they are formally deprecated and removed after adequate notice.

Deprecation headers — Use Deprecation and Sunset HTTP headers to signal that an endpoint will be removed and when clients should migrate.

Semantic versioning for clients — Communicate changes clearly in a changelog. Provide migration guides for major version changes.

Both JSON and XML can be used as data formats in REST APIs, but JSON has become the dominant choice for modern web APIs:

JSON (JavaScript Object Notation): Lightweight and compact — less verbosity means smaller payloads. Native to JavaScript, parsed natively by browsers. Human-readable and easier to debug. Fast parsing in most languages. Supports arrays, objects, strings, numbers, booleans, and null natively.

XML (eXtensible Markup Language): More verbose due to opening and closing tags — larger payload sizes. Supports attributes and namespaces, useful for complex document structures. Built-in schema validation with XSD (XML Schema Definition). Better suited for document-centric data (e.g., contracts, invoices). Required by some enterprise/legacy systems (SOAP APIs always use XML).

When to use XML in REST: Integrating with legacy enterprise systems that require XML. When you need strict schema validation. When the data is inherently document-like (e.g., regulatory submissions). For modern consumer APIs, JSON is almost always the right choice.

Technically yes, a REST API can be implemented statelessly — but doing so violates one of REST's core constraints (Statelessness) and is considered non-RESTful.

What stateless means in REST: Each request must contain all information needed to process it — the server should not rely on stored session state between requests. Authentication tokens, user context, and request parameters should all be present in each request.

Why REST mandates statelessness: Scalability — any server instance can handle any request without needing shared session state. Reliability — if a server crashes, another handles subsequent requests without state loss. Simplicity — debugging and monitoring are easier when each request is self-contained.

How REST "handles" state: Client-side tokens (JWT, OAuth) carry identity/context on every request. Cookies maintain session IDs (though server-side sessions introduce statefulness). Short-lived resources (shopping carts, multi-step workflows) can be modeled as resources with their own endpoints.

Bottom line: A truly RESTful API is stateless. If your API requires server-side session storage between requests, it deviates from REST principles and gains the drawbacks of stateful systems (sticky sessions, scaling complexity, session synchronization).

Real-Time Communication & WebSockets Questions

Real-time communication (RTC) refers to instantaneous data exchange between systems with minimal latency. Unlike traditional request-response models where data retrieval happens on demand, RTC ensures continuous, live updates without requiring users to manually refresh or trigger requests.

Why it matters: Low latency, seamless user experience, essential for critical applications like chat apps (WhatsApp), live stock market tickers (NASDAQ), multiplayer gaming (Fortnite), and live sports score updates.

WebSockets provide a persistent, full-duplex connection over a single TCP connection, allowing both client and server to send data at any time.

How it works: (1) Client sends an HTTP upgrade request. (2) Server responds with 101 Switching Protocols. (3) A persistent connection is established. (4) Both sides exchange messages asynchronously without re-establishing the connection.

vs. Traditional HTTP: HTTP closes the connection after each request/response cycle and is client-initiated only. WebSockets maintain a single persistent connection with bidirectional communication and minimal overhead.

The WebSocket handshake is the initial request-response exchange that upgrades an HTTP connection to a persistent WebSocket connection:

Step 1 — Client sends an HTTP Upgrade request: The client sends a standard HTTP GET with special headers: Upgrade: websocket, Connection: Upgrade, and a Sec-WebSocket-Key (a random base64 value used for validation).

Step 2 — Server responds with 101 Switching Protocols: If the server supports WebSockets, it responds with HTTP 101 and a Sec-WebSocket-Accept header (the server's hash of the client's key), confirming the upgrade.

Step 3 — Persistent connection established: After the handshake, the connection is no longer HTTP — both sides use the WebSocket framing protocol to exchange messages bidirectionally at any time. Either party can close the connection by sending a close frame.

Why the key exchange matters: The Sec-WebSocket-Key / Sec-WebSocket-Accept mechanism prevents caching proxies from incorrectly treating a WebSocket connection as a regular HTTP response.

Persistent Connection — WebSockets keep a single connection open for the entire session, eliminating the overhead of repeatedly creating and closing HTTP connections.

Low Latency — Data is pushed to the client the instant it's available, without the client needing to make a new request. Long polling always has the round-trip latency of a new HTTP request between updates.

Lower Overhead — WebSocket frames have minimal headers (as small as 2 bytes) compared to full HTTP headers sent on every long poll request. This dramatically reduces bandwidth usage at scale.

Bidirectional Communication — Both client and server can send messages at any time independently. Long polling is still client-initiated — the server can only respond, not push proactively.

When WebSockets clearly win: Live chat (Slack, WhatsApp), stock market real-time feeds (NASDAQ), multiplayer games (Fortnite), collaborative editing (Google Docs). Long polling remains useful for simpler setups where WebSocket infrastructure isn't available.

Long Polling: The client sends an HTTP request, and the server holds it open until new data is available (instead of responding immediately). Once data arrives, the server responds and the client immediately sends a new request. Example: Gmail uses long polling for new email alerts.

vs. WebSockets: WebSockets use a single persistent connection with low overhead and low latency, ideal for high-frequency updates. Long polling uses multiple HTTP requests with higher overhead and higher latency, better for less frequent updates or environments where WebSockets aren't supported.

Use long polling when: WebSockets are not supported (e.g., older browsers, firewalls blocking WebSockets), limited real-time needs (notifications don't require instant updates), RESTful API integration (long polling works with standard HTTP), or simpler backend infrastructure is preferred (no WebSocket server needed). Examples: social media feed updates (Facebook), email notifications (Gmail).

Automatic Reconnection — Most WebSocket clients implement reconnection logic in case of failure.

Heartbeat Mechanism — WebSockets use ping/pong messages to detect connectivity issues and keep connections alive.

Backoff Strategies — Clients retry connections with increasing time intervals (exponential backoff) to avoid overwhelming the server during recovery.

Example: Slack automatically reconnects WebSocket connections when users switch between Wi-Fi and mobile data.

Yes, but WebSockets require sticky sessions (session affinity) or connection-aware load balancing to maintain state, since a WebSocket connection must stay with the same backend server for its lifetime.

Sticky Sessions — Ensures a WebSocket connection always routes to the same server.

Reverse Proxy (Nginx/HAProxy) — Supports WebSocket proxying by forwarding the Upgrade header.

WebSocket Gateways — AWS API Gateway manages WebSocket connections at scale.

Maintaining state across servers — Requires sticky sessions or session persistence.

Handling large concurrent connections — Millions of open connections consume significant memory.

Load balancing issues — WebSockets require intelligent routing (sticky sessions).

Connection failures — Requires reconnection logic and backoff strategies.

Solutions: Use Redis Pub/Sub or Kafka for message broadcasting across multiple WebSocket servers. Implement sticky sessions in load balancers. Use WebSocket gateways like AWS API Gateway.

API Gateway Questions

An API Gateway is a server that acts as an intermediary between clients and backend services. It handles request routing, authentication, rate limiting, caching, and response transformation. It is used to manage API traffic efficiently, improve security, reduce backend load, and ensure scalability.

An API Gateway provides additional features beyond load balancing: authentication, rate limiting, caching, and API composition. A Load Balancer simply distributes network traffic among multiple backend servers. Load Balancers operate at Layer 4/7 (network/transport), while API Gateways operate at Layer 7 (application layer) enabling more advanced request handling.

Security — Enforces authentication and authorization for all backend services.

Rate Limiting & Throttling — Controls traffic to prevent abuse and maintain stability.

Load Balancing — Distributes requests across multiple services.

Caching — Reduces backend load and speeds up response times.

Request Transformation — Converts between formats (e.g., REST to gRPC, JSON to XML).

Monitoring & Logging — Tracks API performance, usage, and security threats in one place.

API Gateways enforce security by verifying credentials/tokens before forwarding requests to backend services. If authentication fails, the request is denied at the gateway before it reaches any backend service.

API Keys — Unique identifiers required for API access.

OAuth 2.0 & JWT — Used for secure access management and stateless authentication.

mTLS (Mutual TLS) — Ensures encrypted communication between clients and APIs.

LDAP & SAML — Used for enterprise authentication.

Rate Limiting — Restricts the number of API calls per user/IP per second, minute, or hour. Example: a user can make only 100 requests per minute.

Throttling — Allows excess requests but slows down response times instead of immediately rejecting them. Useful for handling traffic spikes gracefully.

Burst Limits — Temporary high limits that adjust dynamically based on usage patterns.

Common algorithms: Token Bucket (tokens consumed per request, replenished periodically), Leaky Bucket (processes requests at a fixed rate), Fixed Window and Sliding Window Counters (limit requests per time window).

An API Gateway is especially useful in microservices when: multiple services need a unified entry point, security and authentication must be enforced centrally, rate limiting and caching are necessary for stability, request transformation between formats (REST, GraphQL, gRPC) is needed, and API monitoring and analytics are important. Example: In an e-commerce system, an API Gateway routes requests to separate microservices for authentication, product catalog, and payments.

Single Point of Failure — API Gateway failure disrupts all traffic. Solution: Deploy multiple instances with failover support.

Increased Latency — Extra processing (auth, transformation, logging) slows requests. Solution: Enable caching, optimize configurations, minimize unnecessary processing.

Complexity in Microservices — Managing many API routes and policies is challenging. Solution: Use an API Gateway management platform (Kong, Apigee, AWS API Gateway).

Scalability Issues — Poorly designed gateway can become a bottleneck. Solution: Use horizontal scaling with distributed API Gateway nodes.

Versioning & Backward Compatibility — Managing API versions is complex. Solution: Implement versioning strategies (e.g., /v1/resource, /v2/resource).

Distributed Architecture — Multiple API Gateway instances behind a Load Balancer to handle traffic spikes.

Auto-Scaling — Dynamically adjust resources based on demand (e.g., Kubernetes-based deployment).

Rate Limiting & Throttling — Prevent excessive API calls from overloading the system.

Caching — Reduce backend workload by caching frequently requested responses.

High Availability — Multi-region deployment with failover mechanisms.

Monitoring — Track errors, response times, and security threats with observability tools.

Example: A global video streaming service like Netflix uses a CDN-enabled API Gateway to route requests to the nearest data center, improving latency.

Interview Prep

Modern API Protocols Interview Questions

Key interview questions covering gRPC, GraphQL, and how they compare to REST in system design scenarios.

gRPC & GraphQL Questions

REST uses standard HTTP methods (GET, POST, PUT, DELETE) with stateless, resource-based URLs. Simple and widely supported but can suffer from over-fetching and under-fetching.

gRPC (Google Remote Procedure Call) uses HTTP/2 and Protocol Buffers (binary serialization). It is fast, strongly typed, and supports bidirectional streaming — ideal for microservices and internal service communication where performance matters.

GraphQL exposes a single endpoint where clients specify exactly the data they need via a query language. This eliminates over-fetching and under-fetching and is ideal for complex frontends with varying data requirements.

When to choose: Use REST for simplicity and broad compatibility. Use gRPC for high-performance, low-latency internal APIs. Use GraphQL when clients need flexible, precise data queries.

Choose gRPC when:

Performance is critical — gRPC uses Protocol Buffers (binary format) instead of JSON, making serialization/deserialization significantly faster and payloads smaller.

Bidirectional streaming is needed — gRPC supports full-duplex streaming over a single HTTP/2 connection, unlike REST which is request-response only.

Microservices communicate internally — gRPC is excellent for internal service-to-service calls where type safety and efficiency outweigh human-readability.

Multi-language environments — gRPC auto-generates client stubs from `.proto` schema files across many languages, reducing integration effort.

Real-time & IoT systems — Low latency and streaming capabilities make gRPC a natural fit for real-time data pipelines and IoT device communication.

Advantages:

Clients fetch exactly the data they need — no over-fetching or under-fetching. A single endpoint reduces the API surface area. Strong schema enables auto-generated documentation and type-safe client code. Enables rapid frontend iteration without requiring backend API changes.

Trade-offs:

Complex queries can be expensive — deeply nested queries may hammer the database. Mitigate with query depth limiting and query complexity analysis.

N+1 query problem — resolving nested fields can trigger multiple database calls. Solve using DataLoader (batching) patterns.

Caching is harder — REST benefits from HTTP-level caching by URL, but GraphQL POST requests have dynamic bodies, making CDN/proxy caching more complex.

Security surface — flexible queries mean clients can ask for more data than intended; requires field-level authorization and rate limiting.

gRPC supports multiple authentication mechanisms built into its transport layer:

TLS/SSL — gRPC runs over HTTP/2 and uses TLS by default to encrypt all communication between services.

Token-Based Auth (JWT/OAuth2) — Clients pass tokens via gRPC metadata headers (similar to HTTP headers). The server validates the token on every call.

mTLS (Mutual TLS) — Both client and server present certificates, ensuring both sides of the connection are authenticated. Common in service mesh architectures (e.g., Istio).

Interceptors — gRPC supports server-side and client-side interceptors (middleware) to centralize auth logic, logging, and retry policies without modifying individual service code.

DataLoader / Batching — Group multiple database lookups triggered by nested resolvers into a single batched query to avoid the N+1 problem.

Persisted Queries — Clients send a hash instead of the full query string; the server maps hashes to pre-approved queries. This reduces payload size and enables CDN caching.

Schema Stitching / Federation — Split the schema across multiple microservices (Apollo Federation). Each service owns its part of the schema; a gateway merges them for clients.

Query Complexity Limits — Assign a cost to each field and reject queries that exceed a total complexity budget, preventing expensive queries from overloading servers.

Response Caching — Cache resolver results at the field level (e.g., using Redis) for data that doesn't change frequently.

Rate Limiting — Apply per-client rate limits at the GraphQL layer or API Gateway to prevent abuse by any single consumer.

Protocol Buffers (Protobuf) is Google's binary serialization format used as gRPC's default data encoding instead of JSON.

Why Protobuf over JSON?

Size — Protobuf messages are 3–10× smaller than equivalent JSON because field names are replaced with small integer tags and data is encoded in binary.

Speed — Binary parsing is significantly faster than text parsing; benchmarks show Protobuf serialization is 5–10× faster than JSON.

Type Safety — Schemas are defined in `.proto` files and compiled to strongly-typed code in multiple languages, eliminating whole classes of runtime type errors.

Schema Evolution — Fields are identified by number, so new fields can be added without breaking existing clients (forward & backward compatibility).

The trade-off: Protobuf messages are not human-readable, making debugging harder — tools like `grpcurl` or Protobuf reflection are needed to inspect messages.

Further Reading

Quick Check

A live sports-score widget needs one-way updates pushed every few seconds, with automatic reconnection, to millions of browsers. Best transport?

SSE is the fit: server-to-client only, plain HTTP (CDN/proxy friendly), auto-reconnect with event replay — and far simpler to operate at fan-out scale than millions of stateful sockets.
Try It

Interactive: REST vs WebSocket Overhead

Send HTTP REST requests and watch the TCP+TLS handshake overhead on every call. Then open a WebSocket — pay the connection cost once, and see subsequent frames arrive in ~2ms instead of 120ms+.