Engineering Atlas › System Design Interviews

Networking essentials for design interviews

14 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

The life of a request

A browser wants https://api.shop.example/cart. A recursive resolver returns the address, walking root, .example and authoritative servers on a cache miss. The client opens a TCP connection (SYN, SYN-ACK, ACK), runs a TLS handshake, then sends the HTTP request. Each step is at least one round trip, and only the last one does useful work.

TCP and UDP

TCP numbers every byte, acknowledges receipt, retransmits losses, and slows down under congestion. The application sees a stream with no message boundaries, so protocols on top add framing. UDP sends independent datagrams with no ordering, retransmission or congestion control. DNS, video calls, games and QUIC use it because a late packet is worse than a lost one, or because they want their own reliability rules.

HTTP/1.1, HTTP/2, HTTP/3

HTTP/1.1 keeps connections alive but answers requests in order, so browsers open about six connections per host to fetch in parallel. HTTP/2 splits each request into frames tagged with a stream ID and interleaves many streams on one connection, with compressed headers. Its weakness is TCP itself: one lost segment blocks every stream behind it until the retransmit arrives. HTTP/3 runs over QUIC, where each stream is delivered independently, the transport and TLS handshakes are combined, and connections survive a change of client IP because they are identified by connection IDs rather than the address tuple. Two spec changes are easy to miss. The current HTTP/2 spec is RFC 9113 (June 2022), which replaced RFC 7540 and deprecated its stream-priority scheme, so do not design around priority trees. HTTP/3 is RFC 9114, and it swaps HPACK for QPACK because HPACK's strictly ordered table updates would bring head-of-line blocking back (RFC 9114).

Load balancing at L4 and L7

An L4 balancer sees IP addresses, ports and protocol. It picks a backend per connection, by hash or least connections, and forwards packets without reading them, so it is fast and protocol-agnostic. An L7 balancer terminates the connection, decrypts TLS, parses HTTP, and routes each request by host, path or header; it can retry, rate limit and spread HTTP/2 streams from one client across many backends. The common shape is DNS or anycast to a region, an L4 tier for packet capacity, then an L7 proxy tier such as Envoy or NGINX. Cloud guides draw the same split along two axes, global versus regional and HTTP versus non-HTTP, and warn that DNS-based global balancing fails over slowly because resolvers and clients ignore TTLs (Azure Architecture Center).

Down the stack

Layer 1The contract

TCP promises that bytes arrive in order, without gaps or duplicates, or the connection fails. It does not promise that the peer application processed them: an ACK means the kernel buffered the data. A timeout leaves the outcome unknown, so retried requests must be idempotent. TLS promises confidentiality, integrity and authentication of the server's name against a trusted CA, and TLS 1.3 0-RTT data has no replay protection, so it must only carry idempotent requests. DNS promises nothing about freshness beyond the TTL, and many clients and resolvers hold records longer than asked. An L7 balancer promises per-request routing but may retry on your behalf, which can double a non-idempotent POST.

Layer 2The mechanism

TCP. The handshake exchanges initial sequence numbers and options such as MSS, window scale and SACK. The sender may have min(cwnd, rwnd) bytes unacknowledged. Slow start begins with an initial window of about 10 segments, capped at min(10*MSS, max(2*MSS, 14600)) bytes (RFC 6928), so about 14.6 KB, and doubles each round trip until loss or a threshold, after which CUBIC or BBR govern growth. CUBIC, now RFC 9438 (2023), is the default in the Linux, Windows and Apple stacks. Three duplicate ACKs trigger fast retransmit; silence triggers a retransmission timeout. The standard asks for a 1-second minimum, but Linux uses 200 ms (tcp_rto_min_us), which is where many mysterious 200 ms tail spikes come from. The side that closes first holds the socket in TIME_WAIT for 60 seconds on Linux.

TLS 1.3. The client sends ClientHello with a key_share guess; the server answers ServerHello, its key share, EncryptedExtensions, Certificate, CertificateVerify and Finished in one flight. That is one round trip, versus two for TLS 1.2. Resumption with a pre-shared key allows 0-RTT early data, which the spec says has no guarantee of non-replay between connections.

HTTP/2 and QUIC. HTTP/2 sends HEADERS, DATA, SETTINGS, WINDOW_UPDATE and RST_STREAM frames, with HPACK header compression and per-stream flow control. QUIC packets carry a connection ID and packet numbers that are never reused, so a retransmission is unambiguous; stream frames carry offsets so each stream reassembles independently.

L4 balancers. A connection table keyed by the 5-tuple pins each flow to a backend. Google's Maglev (NSDI 2016) and similar designs use a consistent hashing table so that any balancer instance picks the same backend for a flow, which lets the balancer tier scale horizontally behind ECMP routing. Direct server return lets backends reply straight to the client, so the balancer only carries the small request direction.

Layer 3The machine

A 1,500-byte Ethernet frame carries a 1,460-byte TCP payload after 40 bytes of IP and TCP headers (less with timestamps). The NIC hashes flows across receive queues, one per core. The kernel holds a SYN queue and an accept queue per listening socket; the accept backlog is capped by net.core.somaxconn, 4096 since Linux 5.4 (128 before). When it overflows, Linux does not send an error by default (tcp_abort_on_overflow is off): it silently drops the handshake's final ACK and new SYNs, so clients see stalls of a second or more rather than refusals. Outgoing connections use ephemeral ports from ip_local_port_range, typically 32768 to 60999, so one proxy talking to one backend address can hold about 28,000 connections at once; with 60 seconds of TIME_WAIT that caps fresh connections around 470 per second unless you reuse connections. Linux only reuses TIME_WAIT ports for loopback traffic by default (tcp_tw_reuse=2), so pooling is the real fix. TLS handshakes cost on the order of a millisecond of server CPU (hedge: depends on key type), while bulk AES-GCM runs at gigabytes per second per core.

Physics sets the floor. Fibre carries light at about 200,000 km per second, so New York to London (5,600 km) has a best-case RTT near 56 ms, and real routes measure 70 to 80 ms.

Why it's built this way

  1. Packets get lost and reordered on shared links, so TCP adds sequence numbers, ACKs and retransmission.
  2. Senders that ignore loss collapse shared links, so TCP probes capacity with slow start and backs off on loss, which makes new connections slow.
  3. Handshakes cost round trips that bandwidth cannot buy back, so protocols merged them: TLS 1.3 cut one, QUIC folded transport and crypto together.
  4. Multiplexing on one ordered stream inherits its head-of-line blocking, so HTTP/3 moved streams into the transport.
  5. Reading every request costs CPU, so packet-level L4 balancing absorbs volume cheaply and L7 is applied only where request-aware routing pays.

Diagram

sequenceDiagram
    participant C as Client
    participant R as DNS resolver
    participant L as L7 load balancer
    participant B as Backend
    C->>R: A? api.shop.example
    R-->>C: 203.0.113.10 (TTL 60)
    C->>L: TCP SYN
    L-->>C: SYN-ACK
    C->>L: ACK + TLS ClientHello
    L-->>C: ServerHello, Certificate, Finished
    C->>L: Finished + GET /cart
    L->>B: GET /cart (pooled connection)
    B-->>L: 200 OK
    L-->>C: 200 OK

Worked example

A shopper in Sydney calls an API hosted in Virginia, about 200 ms RTT (hedge: varies by route). The JSON response is 100 KB.

Cold, with no edge: DNS through a nearby resolver with a cached answer costs about 5 ms. TCP takes 1 RTT, TLS 1.3 another, the request a third: 600 ms before the first byte. With TLS 1.2 it is 800 ms. Then slow start: the first flight carries about 14.6 KB, the next 29 KB, then 58 KB, so 100 KB needs two more round trips after the first, another 400 ms. Total about one second.

Now terminate TCP and TLS at an edge proxy in Sydney, 10 ms away, which keeps warm connections to Virginia. Handshakes cost 2 RTTs of 10 ms, and the request crosses the ocean once on a connection whose congestion window is already large: about 20 + 200 + 10 = 230 ms. The 4x gain comes from moving round trips, not from adding bandwidth.

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
HTTP/1.1Universal, simple to debugOne request at a time per connectionInternal tools, legacy clients
HTTP/2 over TCPMultiplexing, header compression, gRPCTCP head-of-line blocking on lossy linksService to service, most APIs
HTTP/3 over QUICNo transport HOL blocking, faster setup, connection migrationUDP sometimes blocked, more CPU, newer toolingMobile and long-haul clients
L4 load balancerMillions of packets per second, any protocolNo request-level routing or retriesFront tier, databases, non-HTTP
L7 load balancerPath routing, retries, auth, observabilityTLS and parsing CPU, extra hopHTTP APIs and microservices
DNS-based balancingGlobal, cheap, no hopTTLs ignored, slow failoverChoosing a region

Pitfalls in production

Leadership lens

Check yourself

  1. Why can HTTP/2 be slower than HTTP/1.1 with six connections on a lossy mobile network?

    Show answer

    One lost TCP segment stalls every multiplexed stream on the single connection, while six connections only stall the one that lost a packet and grow six congestion windows.

  2. A load balancer acknowledges your POST, then the connection resets. Did the order get created?

    Show answer

    Unknown. A TCP ACK only means bytes were buffered; the request may or may not have run, so the retry needs an idempotency key.

  3. Why does a gRPC service behind an L4 balancer end up with one hot pod?

    Show answer

    L4 balances connections, and gRPC reuses one long-lived HTTP/2 connection for all requests, so every request from that client lands on the same pod.

  4. Why is TLS 1.3 0-RTT unsafe for a payment request?

    Show answer

    Early data can be replayed by an attacker who captured it, and the server cannot tell, so it must only carry idempotent requests.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →