Interview conceptSystem Design

Robust Networking, REST, And Concurrency Control

Asked of: Software Engineer

Last updated

Layered boxes-and-arrows architecture: client -> edge (API gateway, TCP framing, rate limiter) -> app servers (parser, idempotency, concurrency control) -> data (cache, DB primary+replica, message broker); arrows show primary flow, retries, timeouts, and optimistic/pessimistic locks.

What's being tested

This area tests whether a Software Engineer can build APIs and networked components that behave correctly under retries, partial failures, concurrent writes, malformed input, and changing scale. Apple interviewers are probing for practical distributed-systems judgment: not just “can you write the happy path,” but can you preserve correctness when clients disconnect, requests are duplicated, two users update the same resource, or a backend times out after committing. Strong answers combine clean API design, explicit failure semantics, bounded resource usage, and testability.

Core knowledge

  • TCP stream framing is mandatory because sockets deliver bytes, not messages. A robust reader needs a delimiter protocol, fixed-length header, or length-prefixed frame; it must handle partial reads, coalesced messages, EOF, malformed lengths, and maximum frame sizes.

  • Internal buffering separates network reads from message parsing. Keep a mutable byte buffer, append chunks from recv, parse complete frames, and retain leftovers. Complexity should be O(n)O(n) over bytes read; avoid repeated slicing that turns parsing into O(n2)O(n^2).

  • Timeouts and cancellation are correctness features, not polish. Use connect, read, write, and overall request deadlines; distinguish timeout from EOF and protocol errors. Server-side handlers should honor client cancellation to avoid wasted work and stale writes.

  • REST idempotency means repeated identical requests have the same intended effect. GET, PUT, and DELETE are naturally idempotent when modeled well; POST usually needs an idempotency key, commonly stored with request hash, status, response body, and expiration.

  • Optimistic concurrency control uses a resource version, updated_at, monotonically increasing version, or ETag. Clients send If-Match: <etag>; the server updates with WHERE id = ? AND version = ?, returning 412 Precondition Failed or 409 Conflict if stale.

  • Pessimistic locking can be appropriate for scarce, high-value state, but it reduces throughput and risks deadlocks. Prefer short database transactions, deterministic lock ordering, and lock timeouts; avoid holding locks while calling external services.

  • Retry safety requires classifying failures. Retry 429, 503, connection resets, and timeouts with exponential backoff and jitter, e.g. delay=min(cap,base2k)+random(0,jitter)delay = min(cap, base \cdot 2^k) + random(0,jitter); do not blindly retry non-idempotent mutations.

  • Error contracts should be stable and machine-readable. Use appropriate HTTP statuses: 400 validation, 401 unauthenticated, 403 unauthorized, 404 not found, 409 conflict, 412 precondition failed, 422 semantic validation, 429 rate limited, 5xx server failure.

  • Authentication and authorization are separate checks. JWT, session tokens, or mTLS prove identity; authorization verifies the caller can perform the action on that resource. Enforce authorization server-side even if the client hides unavailable actions.

  • Rate limiting protects shared services from overload and abuse. Common algorithms include token bucket, leaky bucket, and fixed/sliding windows. Return 429 with Retry-After; choose limits per user, device, IP, API key, or resource depending on abuse mode.

  • Snapshot semantics require a clear read model. A snapshotable key-value store can use copy-on-write maps, immutable persistent data structures, or multi-version concurrency control; reads at snapshot s should see the latest version s\le s for each key.

  • Python concurrency has subtle guarantees. dict and list operations may be individually safe from interpreter crashes under the GIL, but compound operations like “check then insert” are not atomic. Use threading.Lock, asyncio.Lock, queues, or single-writer ownership.

Worked example

For Implement a robust REST API method, a strong candidate should first clarify the resource, mutation semantics, caller identity, and whether duplicate submissions are possible: “Is this create, update, or money-moving? Do clients retry after timeouts? Do we need exactly-once effect or just idempotent observable behavior?” Then declare assumptions, such as using Postgres, JSON over HTTP, authenticated users, and a versioned row model. Organize the answer around four pillars: API contract, validation/authz, concurrency/idempotency, and failure handling/testing.

For a create-like POST, use an Idempotency-Key header scoped to user and endpoint; store the request hash and final response in an idempotency_requests table with a unique constraint on (user_id, key). If the same key arrives with a different request hash, return 409 Conflict; if the original is still processing, either wait briefly or return 409/202 depending on product semantics. For update-like methods, prefer PUT /resource/{id} or PATCH /resource/{id} with ETag and If-Match, implemented via an atomic conditional update.

Flag the key tradeoff explicitly: storing full responses makes retries simple and consistent, but increases storage and requires TTL cleanup; recomputing responses saves space but risks drifting behavior if state changes. Testing should include duplicate request races, stale version updates, auth failures, validation failures, database transaction rollback, and retry-after-commit scenarios. Close by saying that with more time, you would add observability: structured logs with request IDs, metrics for 409/412/429, latency percentiles like p95 and p99, and alerts on idempotency-store failures.

A second angle

For Implement a robust socket message reader, the same reliability mindset applies, but the API boundary is lower-level: the danger is assuming one recv equals one logical message. The candidate should define the wire protocol first, usually a length-prefixed frame with a fixed-size header and a maximum allowed body size. Concurrency control shifts from HTTP row versions to buffer ownership: one reader should own the socket buffer, parse complete frames, and hand immutable messages to worker threads or an event loop. Failure semantics must be precise: EOF with an empty buffer is clean close, EOF mid-frame is a protocol error, oversized length is rejection, and timeout is distinct from malformed input. Testing should simulate fragmented headers, multiple messages in one read, zero-byte reads, slowloris behavior, and malicious length values.

Common pitfalls

Pitfall: Treating retries as a transport concern only.

A tempting answer is “the client can retry on timeout,” but this can double-charge, create duplicate rows, or overwrite newer data. A better answer names which operations are safe to retry, adds idempotency keys or conditional updates, and defines what the server returns for duplicate in-flight or completed requests.

Pitfall: Hand-waving “use locks” without specifying scope.

Locks can protect in-memory structures inside one process, but they do not solve races across multiple app servers. For REST methods backed by a database, use unique constraints, transactions, row versions, and conditional writes; for in-process buffers, use ownership or explicit mutexes with minimal critical sections.

Pitfall: Over-indexing on the happy-path object model.

Candidates often design elegant endpoints but omit size limits, malformed input handling, EOF behavior, authorization, rate limits, and observability. Apple-style systems questions reward engineers who state invariants and failure modes: maximum payload, timeout policy, error mapping, transaction boundary, and tests for adversarial cases.

Connections

Interviewers may pivot from here into distributed transactions, eventual consistency, database isolation levels, or cache invalidation. They may also ask for implementation-level details in Python, Swift, Java, or Go, especially around thread safety, async I/O, and memory usage under high concurrency.

Further reading

Featured in interview prep guides

Practice questions

Related concepts

Robust Networking, REST, And Concurrency Control — Tech Interview Concept | PracHub