Picking a default tech stack for answers
Open in the interactive app: practice, quiz, mock interview →In one minute
- A concept says what the design needs; a technology is the thing you would run. Interviewers want a named tool, the requirement that chose it, and the limit that would make you switch.
- Start from a small default stack: a relational database, Redis, object storage, a managed queue, a load balancer and containers. Deviate only when a requirement or a number forces it, and say which.
- Most hard choices are between neighbours: Kafka versus SQS versus RabbitMQ, Redis versus Memcached, Postgres versus DynamoDB versus Cassandra, Flink versus Spark, Lambda versus containers. Know the one property that separates each pair.
- Know each tool's contract: ordering, durability and consistency guarantees, and where it quietly loses data.
- Under time pressure, use one sentence per choice: "X because R; its limit L does not bite here because N."
How it works
The interview toolkit
Each technology below has a deep-dive lesson in this course.
| Role | Default | Switch to | When | Go deeper |
|---|---|---|---|---|
| System of record | PostgreSQL or MySQL | DynamoDB, Cassandra, MongoDB | Key access at very high write rates | PostgreSQL, MySQL, DynamoDB, Cassandra, MongoDB, Choosing the Right Database |
| Cache | Redis | Memcached | Plain strings, multithreaded nodes | Redis, Memcached |
| Blobs | S3 (object storage) | none at interview scale | Never blobs in database rows | S3 |
| Search | Elasticsearch | Postgres full-text for small corpora | Relevance ranking, fuzzy match, facets | Elasticsearch |
| Task queue | SQS | RabbitMQ | Routing rules, priorities | SQS, RabbitMQ |
| Event log | Kafka | SQS if no replay is needed | Per-key order, many consumers, replay | Kafka |
| Stream processing | Flink | Kafka Streams, Spark Structured Streaming | Stateful event-time windows | Flink |
| Batch processing | Spark | SQL on a warehouse | Large joins over history | Spark |
| Compute | Containers on Kubernetes | AWS Lambda | Spiky, short work under 15 minutes | Docker, Kubernetes, AWS Lambda |
| Edge and proxy | Nginx or a managed load balancer | Envoy | Mesh routing and telemetry | Nginx |
| Coordination | ZooKeeper or etcd | A conditional write in your database | Leader election, small critical metadata | Zookeeper |
| Metrics | Prometheus | Managed equivalents | Pull-based scraping, alerting | Prometheus |
Database choice has its own lesson, Choosing the Right Database; this chapter covers the rest of the stack and how the pieces fit together.
The default stack
Most interview systems can be drawn with six pieces: a load balancer, stateless services in containers, PostgreSQL, Redis, S3 and a queue. Starting there has two benefits. It is honest, because many real companies run exactly this far past the scale people expect. And it makes every deviation visible: when you replace Postgres with Cassandra, the interviewer hears that something in the requirements forced it, and expects you to say what.
Telling the neighbours apart
The hard choices are between tools that look similar. One property usually separates them.
- Kafka versus SQS versus RabbitMQ. Kafka is a log: messages stay after reading, consumers track their own offsets, and order holds per partition, so several consumers can replay a stream. SQS is a managed queue: a message is hidden while one consumer works on it and deleted when done, which fits independent tasks. RabbitMQ routes messages through exchanges and bindings by rules, with per-message acknowledgement. The lines have blurred: Kafka 4.2 (February 2026) made share groups, its queue-style consumption where several consumers split one partition's records, production-ready (Kafka 4.2 release), and RabbitMQ 4.0 removed classic mirrored queues in favour of Raft-based quorum queues, with streams as its own replayable log (RabbitMQ docs). Say which contract you need, not just which brand.
- Redis versus Memcached. Redis has data structures, persistence and replication, so it doubles as a leaderboard, rate limiter or lock store. Memcached is a multithreaded string cache and nothing else, which is sometimes exactly right. Know the licensing story too: Redis moved to source-available licences in March 2024, the Linux Foundation forked the last BSD-licensed release as Valkey (Valkey), and Redis 8 added the open-source AGPLv3 as an option in May 2025 (Redis). Managed offerings now often say Valkey where they used to say Redis.
- Flink versus Spark. Flink processes each event as it arrives, with state and event-time windows, so it suits real-time aggregates. Spark processes bounded datasets in stages, so it suits nightly jobs and backfills.
- Lambda versus containers. Lambda scales to zero and bills per millisecond, but caps an ordinary invocation at 900 seconds (15 minutes) and adds cold starts (Lambda docs). Two newer options soften the cap: Lambda Managed Instances allow up to 90 minutes for asynchronous and event-source invocations, and durable functions checkpoint a multi-step workflow so it can span up to a year (Lambda durable functions). Containers cost money while idle but give steady latency and long-lived connections.
- ZooKeeper versus your database. A dedicated coordination service gives sessions, ephemeral nodes and watches. Often a conditional update in the database you already run is enough, and is one fewer system to operate.
Choosing under time pressure
You will not have time to compare every option. Spend comparison time only where the choice is contested or load-bearing, and state everything else as a default in one sentence. Name the tool's sharpest limit in the same breath, because that is the follow-up you will get: "SQS for thumbnail jobs, because each job is independent and we want no brokers to run; it delivers at least once, so the worker writes thumbnails to a deterministic key and duplicates are harmless."
Common mistakes
- Naming a technology without the requirement that chose it.
- Using Kafka as a task queue for a few hundred jobs per second, or SQS where consumers need to replay history.
- Treating Redis as a durable database without discussing persistence and failover loss.
- Picking a tool by brand rather than by contract, then being unable to answer "what happens when it fails?"
Down the stack
Layer 1The contract
Each tool makes specific promises, and the interesting ones are the edges. Kafka guarantees order within a partition, not across partitions; with acks=all and min.insync.replicas=2 on a replication factor of 3, an acknowledged write survives one broker loss. Since Kafka 3.0 the producer defaults to acks=all with idempotence on (Apache Kafka 3.0 notes), but min.insync.replicas is still a topic setting you must raise yourself. SQS standard queues deliver at least once with best-effort ordering; a received message is invisible for the visibility timeout (30 seconds by default) and reappears if not deleted, and FIFO queues deduplicate within a 5-minute window and keep order per message group, at up to 300 operations per second per partition, or 3,000 messages with batching (SQS docs). Messages can be up to 1 MiB since August 2025, up from 256 KiB (AWS). Redis replicates asynchronously, so a failover can lose writes the old primary already acknowledged; WAIT narrows but does not close that window. S3 has given strong read-after-write consistency for objects and listings since December 2020 (AWS). Elasticsearch is near real time: a document becomes searchable after the next refresh, every second by default, but a shard that has had no searches for 30 seconds skips background refreshes until the next search arrives, unless you set the interval explicitly (Elasticsearch docs). ZooKeeper linearises writes but serves reads from the connected server, which may lag unless the client calls sync first (ZooKeeper guide). Lambda runs at most 15 minutes per invocation.
Layer 2The mechanism
The contracts follow from the internals. Kafka stores each partition as append-only segment files with sparse offset indexes; followers fetch from the leader, and the in-sync replica set (ISR) decides which writes are committed. Since Kafka 4.0 (March 2025), the first release to run entirely without ZooKeeper, metadata lives in a Raft-based controller quorum (KRaft) (Kafka 4.0 release). SQS hands each receive a receipt handle and a lease; the visibility timeout is that lease. Redis runs commands on one thread around an event loop, which makes every command atomic and makes a slow KEYS * block everyone; persistence uses RDB snapshots taken by a forked child and an append-only file (AOF) with a configurable fsync policy. Elasticsearch buffers writes in memory plus a translog, and a refresh turns the buffer into a new immutable Lucene segment. Flink snapshots operator state by injecting checkpoint barriers into the stream, a variant of the Chandy-Lamport algorithm, so recovery rewinds sources to the last consistent snapshot (Flink docs). Prometheus scrapes targets and writes samples to a WAL and an in-memory head block, cut into two-hour blocks on disk and compacted into larger ones, with 15 days of retention by default (Prometheus docs).
Layer 3The machine
Kafka is fast because it does what disks and kernels are good at: batched, compressed sequential appends into the page cache, and sendfile to move bytes from page cache to socket without a user-space copy (when TLS is off), so a broker sustains hundreds of MB/s on ordinary disks. Redis keeps everything in RAM, so a lookup is a hash probe of about 100 ns plus a network round trip of a few hundred microseconds; its weak spot is fork for snapshots, where copy-on-write can nearly double memory if the write rate is high during the snapshot. Lucene maps segments with mmap, so search speed depends on the page cache holding the hot index, which is why Elasticsearch caps the JVM heap at 50 percent of RAM and now sizes it automatically by default (Elasticsearch docs). Lambda runs each function in a Firecracker microVM; a cold start is typically hundreds of milliseconds to a few seconds depending on runtime and package size. Nginx runs one worker per core, each an epoll event loop, so tens of thousands of idle keep-alive connections cost memory, not threads.
Why it's built this way
Disks are fast sequentially and slow randomly, so durable logs append and compact. Memory is fast but expensive and volatile, so caches are fast and either lose data or pay for snapshots. Coordination costs round trips, so systems push it into small dedicated services or a quorum used only for metadata. Operating a cluster is expensive in engineer time, so managed services win unless a contract they cannot meet forces you to self-host. That is why the default stack is boring and why each deviation needs a reason.
Diagram
flowchart LR
U["Clients"] --> LB["Load balancer / Nginx"]
LB --> API["Stateless services (K8s)"]
API --> R["Redis cache"]
API --> PG["PostgreSQL"]
API --> S3["S3 blobs"]
API --> Q["SQS tasks"]
Q --> W["Workers"]
PG -->|CDC| K["Kafka log"]
K --> F["Flink aggregates"]
K --> ES["Elasticsearch"]
K --> SP["Spark batch"]Worked example
The prompt: a video-sharing service with 500,000 uploads a day averaging 200 MB, 50 million daily viewers, title search, and creator dashboards showing views per minute.
- Uploads. 500,000 × 200 MB is 100 TB a day, straight to S3 via presigned URLs; no service touches the bytes.
- Metadata. About 6 new rows per second and perhaps 50,000 reads per second at peak: PostgreSQL with replicas and Redis in front. Nothing argues for a wide-column store.
- Transcoding. One independent, minutes-long job per upload: SQS with workers on Kubernetes, since long videos exceed Lambda's 15-minute cap. Outputs go to deterministic keys, so at-least-once delivery is safe.
- View counts. 500 million view events a day is about 6,000 per second on average and perhaps 30,000 at peak. Dashboards need per-minute counts and the same events feed recommendations and billing: several consumers and replay point to Kafka, per-minute windows to Flink.
- Search. Typo-tolerant title search points to Elasticsearch, fed from Postgres through change data capture; a second of indexing delay is fine.
Each item is one sentence plus its limit. Expect the push on item 4 ("why not increment a counter in Redis?"); that is where the comparison time goes.
In the wild
- AWS, 2020: adding Kinesis capacity crosses the OS thread limit. Every Kinesis front-end server kept a thread per peer, so adding servers pushed every server past the operating system's thread limit, and shard-map caches could not build. Kinesis was impaired for about 17 hours, taking Cognito, CloudWatch, Lambda, autoscaling and EventBridge with it. AWS moved to fewer, larger servers, added thread alarms and sped up cellularization (AWS).
- Roblox, 2021: one shared Consul cluster takes down everything. Enabling Consul's new streaming feature caused contention on a single Go channel, while BoltDB freelist bloat (a 4.2 GB log with 489 MB live) slowed Raft writes and elections. Every backend service and the monitoring stack depended on that one cluster, so the platform was down for 73 hours. Roblox split Consul per critical service and removed monitoring's Consul dependency (Roblox).
- Discord, 2017: a correct Redis failover, an incorrect client. A cloud live migration knocked a Redis primary offline and the cluster failed over correctly, but API servers hit a known bug in handling the failover. Cache, Cassandra, memory and reconnect failures piled on, and the outage lasted about 2 hours 51 minutes, ending in a full restart that reconnected millions of clients (Discord status).
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Kafka | Replay, per-key order, many consumer groups, high throughput | Partitions to plan, cluster to run, consumer lag to watch | Event streams with several consumers or a need to reprocess |
| SQS | No servers, scales automatically, visibility-timeout retries | No replay, best-effort order (FIFO has throughput limits) | Independent background tasks |
| RabbitMQ | Flexible routing, per-message acks, low latency | Throughput per node lower than Kafka, queues degrade when deep | Routing rules, RPC-style work, priorities |
Pitfalls in production
- Kafka partitions sized for today. The partition count caps consumer parallelism and changing it remaps keys. Size for two to three years of growth and choose keys that spread load.
- Redis as the only copy. A failover loses the last acknowledged writes, or eviction silently drops keys. Keep the source of truth elsewhere, or use persistence and
WAITknowingly. - Visibility timeout shorter than the work. SQS redelivers a message still being processed, so two workers run it. Set the timeout above the p99 job time and extend it for long jobs.
- Elasticsearch as system of record. Mapping changes require reindexing and the cluster can lose writes on misconfiguration. Rebuild it from a durable source.
- Lambda in front of a relational database. Thousands of concurrent invocations open thousands of connections. Put a connection pooler or proxy between them and cap concurrency.
Leadership lens
- Keep a short list of approved technologies per role with an owner for each. Every new entry costs on-call, upgrades and expertise for years.
- In design review, ask for each technology: which requirement chose it, what is its sharpest limit, and who operates it at 3 a.m.?
- Prefer managed services until the bill or a missing guarantee justifies running it yourselves, and write that threshold down.
- Watch for duplicated capabilities (two queues, two caches) that arrive through team-by-team choices; consolidation is cheaper early.
Check yourself
A design uses SQS for order events consumed by billing, analytics and email. Analytics later needs to reprocess last week. What went wrong?
Show answer
SQS deletes messages after processing, so there is no history to replay. A log such as Kafka with retention lets each consumer group re-read from an earlier offset.
Why can a Redis primary failover lose writes that clients saw acknowledged?
Show answer
Replication is asynchronous: the primary acknowledges before replicas have the write, and a promoted replica that missed it becomes the new truth.
A job takes up to 40 minutes. Lambda or containers?
Show answer
Containers, or split the job into steps. Lambda caps an invocation at 15 minutes.
Sources and further study
Primary
- Apache Kafka 4.0 release announcement: the move to KRaft-only clusters and early access to queues.
- Apache Kafka 4.2 release announcement: share groups declared production-ready.
Systems docs
- Elasticsearch: index settings: refresh interval and search-idle shards.
- RabbitMQ: classic queue mirroring: why mirroring was removed and when to use quorum queues or streams.
- AWS Lambda durable functions: checkpoint and replay for long-running workflows.
- ZooKeeper Programmer's Guide: sessions, watches and the exact consistency guarantees.
Books
- Designing Data-Intensive Applications (1st ed.), Martin Kleppmann, chapter 11 "Stream Processing": message brokers versus partitioned logs.
- System Design Interview, Volume 2, Alex Xu and Sahn Lam, chapter 4 "Distributed Message Queue": how a Kafka-like log is built and sized.
Other curricula
- donnemartin/system-design-primer: covers message queues, task queues and back pressure under "Asynchronism", and caches under application caching, without comparing specific products.
- Azure Architecture Center: Understand data models: maps access patterns to store types and warns against letting a search index become the primary store.
Watch
- Building Software Systems at Google and Lessons Learned (Jeff Dean, Stanford): how Google's storage, batch and serving technologies each answered a specific limit of the previous one.
- Mastering Chaos: A Netflix Guide to Microservices (InfoQ): technology choices in a real stack, including caches and the costs of running them.
- What is Apache Kafka? (Confluent): the log model that separates Kafka from a queue.
Free resources
- Hello Interview: Key Technologies: a short interview-focused tour of the usual stack.
- Choose Boring Technology (Dan McKinley): why every new technology spends a limited budget of team attention.
- Choose an Azure messaging service (Azure Architecture Center): events versus messages, a clean framing for queue versus log.
- Kafka documentation: Design: the log, page cache and zero-copy reasoning in the authors' words.
- Amazon SQS visibility timeout: the lease that makes at-least-once delivery work.