Distributed systems and architecture patterns explained in depth.
Why at-least-once is the only delivery guarantee a distributed system gets for free, and how making every operation safe to repeat — via idempotency keys, natural idempotency, or consumer-side deduplication — turns that into something that behaves like exactly-once.
How to keep a business operation that spans several services consistent without a distributed transaction, by sequencing local transactions and compensating actions instead — and what isolation anomalies you give up to get there.
How to give mutual exclusion to a critical section that calls a slow external API, using a database row as a lease-based lock instead of a database transaction or a Postgres advisory lock.
How distributed systems order events and generate IDs when wall clocks cannot be trusted — Lamport timestamps, happens-before causality, total order broadcast, and the practical trade-off between strictly monotonic linearizable sequences and scalable roughly ordered IDs such as Snowflake.
The recency guarantee that makes a replicated object behave like one up-to-date copy — why it is different from serializability, where systems rely on it, how leaders and consensus implement it, and why CAP and network delay make it expensive.
How distributed systems decide what is true when no node can trust only its own view — quorum-based truth, Byzantine faults, realistic system models, safety versus liveness, and the formal and randomized testing techniques used to make fault-tolerant algorithms credible.
How atomic commit works when one transaction touches several nodes or systems, why two-phase commit can preserve all-or-nothing semantics yet block during failures, and why modern architectures often prefer sagas, outboxes, or consensus-backed distributed SQL instead.
Why "is it scalable?" is a meaningless question until you name the load parameter and the dimension of growth — the shared-memory/shared-disk/shared-nothing taxonomy, why there is no generic scalable architecture, and the three separately-optimizable properties that decide whether a system survives its next five years.
Why reliability only means something once you've defined what "working correctly" and "things going wrong" mean for your specific system — and why the distinction between a fault (one component deviating) and a failure (the whole system stopping) is the single most useful piece of vocabulary in the subject.
Why the average response time tells you almost nothing about what your users actually experience, and how percentiles — p50, p95, p99, p999 — describe a distribution of response times honestly enough to put in an SLA.
Two decisions that shape a system before a single line of business logic is written — whether to rent infrastructure or own it, and whether to spread the workload across machines at all — and why "distributed by default" is usually the more expensive of the two mistakes.
The single change that makes almost every other scaling technique possible — moving state out of the server process and into a separate store, so that any instance can handle any request and none of them are irreplaceable.
The two ways to give a system more capacity — bigger machines versus more machines — why they aren't interchangeable, and how autoscaling turns "how many servers do we need" from a manual decision into a policy.
Why single-leader replication needs a way to agree on who the leader is, how Raft/Paxos-style algorithms turn that into a fault-tolerant shared log via quorum voting, and why almost nobody implements consensus from scratch — they reach for a coordination service like ZooKeeper or etcd instead.
What ACID actually guarantees underneath the marketing term, the specific race conditions (dirty reads, lost updates, write skew, phantoms) that weak isolation levels allow through, and why PostgreSQL's "repeatable read" and MySQL's "repeatable read" don't mean the same thing.
Why a node in a distributed system can never fully trust its own judgment about the current time or its own liveness — unreliable networks, unreliable clocks, and process pauses, and why they all point to the same underlying problem.
Why a distributed system can't give you consistency and availability at the same time during a network partition — and why that framing is both essential and incomplete.
Why the only way to know a failover actually works is to trigger the failure that requires it, in production, on purpose — chaos engineering as a disciplined experimental method, not just "randomly break things," including how to bound the blast radius so the experiment doesn't become the incident it was designed to prevent.
Why retrying a failed call the naive way can turn a small blip into a full outage, and the two real fixes — exponential backoff with jitter to stop retries from synchronizing into a thundering herd, and hedged requests to trade a little extra load for a much better tail latency.
How to stop one slow or failing dependency from taking down every service that calls it — by failing fast once a dependency looks unhealthy, and by isolating the resources each dependency can consume so it can only ever exhaust its own slice.
Why every byte that leaves a process has to be encoded, what JSON, Protocol Buffers, and Avro actually put on the wire, and how field tags and writer/reader schema resolution keep old and new versions of your code able to read each other's data.
Why B-trees can't answer "find documents about X" or "find things similar to this," and the index structures that can — multidimensional R-trees, inverted indexes with postings lists, and HNSW graphs over vector embeddings.
Why a warehouse query that sums one column of a billion-row table shouldn't have to read the other ninety-nine — the columnar disk layout behind every modern analytical engine, the compression it unlocks, and the vectorized execution and precomputed aggregates built on top of it.
How the two dominant storage engine families — in-place-updating B-trees and append-only LSM-trees — actually write and read data on disk, and why that hidden choice determines your write throughput, read latency predictability, and disk footprint.
When almost every relationship in your data is many-to-many, the natural model is vertices and edges rather than tables or documents — and a pattern-matching query language like Cypher earns its keep against thirty lines of recursive SQL.
Why the choice between normalized tables and self-contained JSON documents is a question about the shape of your data — trees versus graphs — rather than a question about which technology is more modern.
Why the database serving your checkout flow is the wrong place to compute last quarter's revenue — the opposite access patterns behind OLTP and OLAP, the data warehouse that separates them, and the systems-of-record vs. derived-data distinction that makes the whole pipeline safe to rebuild.
Why a system that stores video files, product catalogs, session state, social graphs, and versioned event history shouldn't put all of them in the same kind of database — matching each data shape and access pattern to the storage engine actually built for it.
How a tree of hashes lets two parties agree that gigabytes of data are identical by comparing one hash, prove a single record's membership in O(log n), and repair only what actually diverged -- with the domain-separation bug that broke Bitcoin along the way.
The full menu of partitioning schemes — key range, hash range, and the multitenancy case — plus the two operational problems everyone hits afterwards: rebalancing shards without making an incident worse, and answering secondary-index queries when the index doesn't line up with the shards.
What you gain and what you give up when more than one node can accept writes — multi-leader topologies for geo-distributed and offline-capable apps, leaderless Dynamo-style quorums, and the conflict detection and resolution machinery (LWW, CRDTs, version vectors) that both approaches force you to build.
Why funneling every write through one designated node is still the default replication model for PostgreSQL, MySQL, MongoDB, and Kafka — and what it costs you in failover danger and read-your-own-writes anomalies.
How to generate unique identifiers at high throughput across many servers without a single database sequence becoming the bottleneck — and how to size an ID for a fixed-length short code.
A hashing scheme that lets a distributed store add or remove nodes while remapping only a small fraction of keys — instead of nearly all of them, as naive modulo hashing does.
Why "just use Redis" isn't always the right default — how Redis's single-threaded, rich-data-structure design and Memcached's multi-threaded, pure-key-value design actually differ in architecture, persistence, and how each one scales.
How to get mTLS, retries, circuit breaking, load balancing, and per-request observability for every service-to-service call in a microservice fleet without writing that logic into every service — by moving it into a proxy that rides alongside each instance, and a control plane that configures every proxy at once.
How to ship a change to millions of users without finding out it was broken from all of them at once — blue-green's instant all-or-nothing switch, canary's gradual traffic shift with a go/no-go decision, and feature flags decoupling "deployed" from "turned on" entirely.
How to design so an entire region can disappear without taking the business down with it — active-passive vs. active-active topologies, the RTO/RPO numbers that actually define "how bad is acceptable," and why a disaster recovery plan nobody has rehearsed is not a plan.
How Dropbox-style sync products handle files too large for a single request and avoid re-transferring bytes the server already has — by splitting files into content-addressed chunks, fingerprinting each one, and syncing only what changed.
Why large files should never be streamed through your application server or stored as blobs in a relational database — and how presigned URLs let clients upload straight to object storage while your database only ever holds metadata.
Why unbounded request rates let a handful of clients degrade service for everyone else, the algorithms (fixed window, sliding window, token bucket) used to cap them, and where in the stack a rate limiter actually belongs.
Why a distributed system needs a single front door that terminates client traffic, routes to the right service, and centralizes cross-cutting concerns like authentication — instead of exposing every microservice directly to the internet.
Why round-robin is only the starting point for load balancing — how weighted algorithms, least-connections, and saturation-aware routing handle uneven servers and uneven requests, and when to balance at the transport layer versus the application layer.
The handful of caching patterns — cache-aside, write-through, write-behind — that cover most interview and production scenarios, plus what a CDN adds on top of an in-datacenter cache.
How an ordered event log can become the write-side backbone for an ecosystem of databases, indexes, caches, warehouses, and ML stores, letting teams rebuild derived state, unify batch and streaming, and reason about correctness end to end instead of hoping every specialized system stays magically consistent.
How stream processors join unbounded event streams with other streams and changing tables, why those joins are time-dependent, and how checkpoints, replay, idempotent writes, and transactions make failures look effectively once rather than exactly magical.
How stream processors turn unbounded event streams into useful results by separating event time from processing time, using watermarks to reason about late data, and choosing the right window type for rolling analytics, monitoring, and materialized views.
How modern batch dataflow engines replace chains of materialized MapReduce jobs with optimized DAGs, and how distributed systems choose among reduce-side, broadcast, partitioned, and merge joins under shuffle, memory, skew, and optimizer constraints.
The three ways encoded data actually travels between processes — stored in a database and read back later, requested synchronously over REST or RPC, or published as an event to unknown consumers — and how each path changes who must agree on the schema, when, and what happens when a step fails partway through.
Why storing the append-only log of everything that happened — instead of a mutable row holding what is true right now — gives you an audit trail for free and lets you build brand-new read models over old data, and how CQRS's projections turn that log into something queryable.
The four-step pattern — split input into records, map each record to a key/value pair, sort by key, reduce each key's group — that turns the classic Unix log-analysis pipeline into a distributed programming model, and why raw MapReduce got replaced by engines that keep intermediate data in memory instead of writing it to disk between every job.
How distributed batch frameworks turn a cluster of machines into something like a distributed operating system — a resource manager, task executors, and a scheduler that run immutable-input, regenerated-output jobs at scale — and why per-task fault tolerance, not whole-job retry, is the entire point of the model.
How traditional JMS/AMQP-style brokers (RabbitMQ, SQS) treat message delivery as destructive and short-lived, how log-based brokers (Kafka, Redpanda, Kinesis) instead treat the log itself as durable storage that consumers replay independently, and why picking between them is really a choice between per-message parallelism and strict ordering with replay.
How tailing a database's own replication log — instead of polling a table or dual-writing — turns every committed change into an ordered event stream, and why that stream, not the table, is arguably the real source of truth.
When separating the read path from the write path is a legitimate scaling technique versus when it's just two databases arguing about which one is telling the truth.
How to atomically update a database and publish an event about that update without a distributed transaction.
How Google's SRE model turns "how reliable should this service be" from an argument into a number — a measured indicator, a target for it, and an error budget that gives product velocity and operational stability a shared, quantitative currency to negotiate with instead of opposing instincts.
Why a request that fans out across dozens of services can't be debugged with logs and dashboards alone, and how a trace — one causally-ordered tree of spans carrying a single context across every hop — answers "where did the time go and what actually happened" for one specific request.
How a Dropbox- or Google Drive-style product keeps a file identical across every device a user owns — the metadata service that tracks versions and namespace separately from the block storage that holds bytes, the notification channel that tells other devices something changed, and why sync conflicts are a product decision, not just an engineering one.
How to turn "design a system for a billion users" into concrete numbers for QPS, storage, and bandwidth in a few minutes of arithmetic — powers-of-two shortcuts, the latency numbers every engineer should have memorized, and why the actual point isn't precision, it's catching a design that's wrong by three orders of magnitude before you build it.
Why a matching engine is the one system where microseconds — not throughput or availability — are the primary constraint, and how price-time priority, a single-threaded in-memory order book per symbol, and an append-only sequenced event log combine to make matching fast, fair, deterministic, and replayable.
How a wallet keeps every user's balance provably equal to the sum of their transaction history — the append-only ledger, the single atomic debit-plus-credit transfer, and the locking, constraints, and idempotency that keep concurrent transfers from creating or destroying money.
Why a payment system's defining constraint is that money must never be lost or double-charged when a call to an external payment provider times out with an unknown outcome — idempotency keys, exactly-once execution, a double-entry ledger, and nightly reconciliation against the provider's settlement file.
Why "what's my rank?" and "who are the top 10?" are two very different queries at millions of players, and how a sorted set — plus sharding, a top-N cache, and read replicas — answers both in logarithmic time.
How a Gmail-scale email service splits into three systems with three different design pressures — accepting mail from the public internet over SMTP, storing petabytes of mailbox metadata and attachments, and serving fast inbox loads and full-text search on top of it.
Why a booking system is one of the rare designs where correctness outranks throughput — the room-type inventory model, the double-booking race between two users buying the last room, and the pessimistic, optimistic, and constraint-based mechanisms that actually stop it.
How to count a billion ad click events per day grouped by ad and time window without double-counting a single one — the streaming path that answers dashboards in minutes, the batch path that produces the numbers advertisers are actually billed for, and the deduplication, watermarking, and reconciliation that keep them honest.
How a Prometheus- or Datadog-style monitoring platform absorbs tens of millions of metric writes per interval into a time-series store while a second, deliberately simpler path evaluates alert rules on the same data in seconds.
How to build a Kafka-style log-based broker from scratch — an append-only on-disk log split into partitions, leader/follower replication with in-sync replicas, consumer groups with committed offsets and rebalancing, and what each delivery semantic (at-most-once, at-least-once, exactly-once) actually costs.
Three unrelated systems hide behind one prompt — a petabyte-scale static tile pyramid served from a CDN, a geocoder that turns text into coordinates, and a shortest-path engine over a continent-sized road graph that Dijkstra cannot touch.
Why showing which friends are nearby is a high-write, real-time fan-out problem rather than an indexed-lookup one — the in-memory location cache, per-user pub/sub channels, and WebSocket push that make ~334K location updates per second turn into ~14M pushes per second without a database in the hot path.
How a "find every business within 5 km of me" query gets answered in milliseconds — why a two-dimensional range scan is the wrong tool, and how geohash, quadtree, and Google's S2 flatten the globe into a one-dimensional index that a database can actually seek on.
Why a video platform is a transcoding and distribution problem rather than a storage problem — turning one uploaded file into a matrix of adaptive-bitrate renditions through a DAG of parallel encoding tasks, then pushing those renditions to edge caches close to viewers.
How a typeahead box returns five ranked suggestions in under 100ms on every keystroke — a trie annotated with precomputed top-k results, built offline by a batch aggregation pipeline, sharded, cached, and shielded by client-side debouncing.
How a Facebook/Instagram/Twitter-style feed splits into two independently scaled pipelines — a write path that fans a post out into millions of precomputed per-user feed caches, and a read path that hydrates a list of IDs into a renderable feed in milliseconds — plus the celebrity hotkey problem that forces a hybrid fan-out model.
How to fan a single notification event out across push, SMS, and email — per-channel queues that absorb slow third-party APIs, retries and dead-lettering that guarantee nothing is lost, and the dedupe, preferences, and rate limits that keep users from turning notifications off entirely.
How a crawler that fetches billions of pages a month stays polite to individual hosts, fresh against a web that changes underneath it, and robust against traps and malformed content — and why the URL frontier, not the downloader, is where the design actually lives.
A worked design for a TinyURL-style service — sizing the write and read load, the two-endpoint API, the 301 vs. 302 redirect trade-off, and why the redirect path is really a cache design problem.
A worked design for a Dynamo/Cassandra-style distributed key-value store — sizing the cluster, partitioning and replicating the keyspace, tuning consistency with N/W/R quorums, and keeping replicas in sync through hinted handoff, Merkle trees, and gossip.
The deep-dive half of a chat system design interview — guaranteeing per-chat message ordering with Kafka, decoupling fan-out from the chat server via CDC, scaling WebSocket delivery with pub/sub, taming WebSocket churn with leased subscriptions, and partitioning/caching storage for billions of users.
A worked system design interview walkthrough for a Slack-like chat product — functional and non-functional requirements, core entities, why WebSockets replace request/response polling, and the high-level design for sending messages, rich media, offline delivery, and message deletion.