Practice this topic in a realistic system design interview
Understanding system design concepts is essential. But you also need to know the technologies that implement those concepts.
When you say "I'd use a cache here," the interviewer might ask "Which caching solution would you choose and why?" Knowing the difference between Redis and Memcached, or when to use Kafka versus RabbitMQ, demonstrates real-world knowledge.
You don't need deep expertise in dozens of technologies. In practice, the same 15-20 tools show up in most system design discussions. Know what each tool is good at, where it becomes painful, and what requirement would make you choose it.
This chapter covers the technologies that matter most, organized by category. For each one, I'll explain what it does, when to use it, and how to talk about it in an interview.
Before diving into specifics, here's the mental map. Every system design essentially combines technologies from these categories:
Think of it this way: your data layer is the source of truth. Your speed layer makes things fast. Your async layer decouples and buffers. And supporting services handle specialized needs like search, file storage, and traffic distribution.
Let's explore each category.
Relational databases are the starting point for many applications. When your data has clear structure, relationships matter, and transactions protect correctness, this is usually where you begin.
PostgreSQL is a reasonable starting point for many interview designs because it gives you relational modeling, mature transactions, good indexing, and enough flexibility to handle some semi-structured data.
PostgreSQL covers the core relational requirements well. Full ACID compliance gives reliable transaction behavior, and MVCC (Multi-Version Concurrency Control) lets readers and writers proceed without blocking each other in common cases.
Its advanced features are often what make it easier to keep using as the system grows:
In interviews: For structured data, PostgreSQL is a reasonable starting point. State the access patterns first, then explain what would force you away from it: extreme write scale, strict multi-region availability, or a data model that is not relational.
MySQL is widely deployed and well understood by many teams. Compared with PostgreSQL, it is often chosen for operational familiarity, straightforward relational workloads, and mature managed-service support.
MySQL's strength is its simplicity and long operational history. The InnoDB storage engine provides solid ACID transactions. Read replicas are straightforward to set up. The ecosystem is broad: every major language has mature MySQL drivers, and every cloud offers managed MySQL.
For read-heavy web applications with straightforward data patterns, MySQL is a practical choice. Its strength is not breadth of features; it is predictability, ecosystem maturity, and a large pool of operational experience.
| Dimension | MySQL | PostgreSQL |
|---|---|---|
| Simple reads | Faster | Slightly slower |
| Complex queries | Limited optimizer | Sophisticated optimizer |
| JSON support | Functional | First-class |
| Replication | Simple, mature | More flexible, more complex |
| Extensions | Limited | Extensive ecosystem |
| Learning curve | Gentler | Steeper |
For a new project, PostgreSQL is often easier to justify because its feature set is broader. MySQL is still a good answer when the workload is simple, the team operates it well, or existing infrastructure already depends on it.
In interviews, either is fine. What matters is that you can explain your reasoning. "I'd use MySQL because this is a read-heavy workload with simple queries, and our team has strong MySQL experience" is a good answer.
NoSQL is a broad category, and "NoSQL" alone doesn't tell you much. The real question is: what type of NoSQL? Document stores, wide-column stores, and key-value stores solve different problems.
The useful skill is knowing which model fits the access pattern.
MongoDB is one of the best-known NoSQL databases. It stores data as JSON-like documents, making it natural for applications that already think in JSON.
The document model is intuitive. A user profile isn't just a row in a table. It's a document with nested addresses, preferences, and order history. In a relational database, that's multiple tables and JOINs. In MongoDB, it's one document you read in a single query.
MongoDB shards automatically by a shard key you choose. Each shard is itself a replica set for high availability. This gives you horizontal scaling with automatic failover.
MongoDB is a good choice for content management systems, product catalogs, and user profiles. Mention it when you see document-shaped data that doesn't have complex relationships.
Cassandra is built for write-heavy, highly available workloads spread across many nodes or regions. It is a good fit when availability and horizontal write scaling matter more than flexible querying.
Cassandra was born at Facebook to solve their inbox search problem, then open-sourced and adopted by large-scale internet companies. Its design assumes nodes will fail and the system should continue serving traffic.
Every node is equal. There's no master. Data is automatically distributed using consistent hashing, and you can tune how many nodes must acknowledge a write (consistency level).
Need stronger consistency? Use quorum reads and writes so the read and write quorums overlap. Need maximum availability and lower latency? Accept fewer acknowledgments, but expect stale reads during failures or replication lag.
Mention Cassandra specifically for time-series data, logging systems, or IoT platforms. It's also a strong choice for anything requiring multi-region writes with high availability.
DynamoDB is AWS's fully managed NoSQL offering. You don't manage servers, replication, or scaling. You create a table, specify your capacity (or let it auto-scale), and AWS handles everything else.
DynamoDB trades flexibility for operational simplicity. You get single-digit millisecond latency at any scale, automatic replication across availability zones, and zero infrastructure to manage. The cost is that you're locked into AWS and must design your data model around DynamoDB's constraints.
Those constraints are significant. DynamoDB is essentially a key-value store with some document features. You have a partition key (and optionally a sort key), and queries are fast only when you know the keys.
Want to query by an arbitrary field? You need a secondary index. Want complex aggregations? You're better off with something else.
Mention DynamoDB for serverless architectures or when operational simplicity is paramount. It's a natural fit for AWS Lambda-based systems and mobile app backends.
Here's a decision framework:
| Database | Best For | Avoid When |
|---|---|---|
| MongoDB | Documents, evolving schemas, rich queries | Complex transactions, strict consistency |
| Cassandra | Write-heavy, time-series, multi-region | Ad-hoc queries, small datasets |
| DynamoDB | Serverless, AWS-native, managed operations | Vendor lock-in concerns, complex queries |
Caching is how you make slow things fast. Your database might take 50ms to answer a query. Redis answers in under a millisecond. At thousands of requests per second, that difference is the gap between a responsive application and a frustrating one.
Redis is often the first cache to consider because it is fast, familiar, and supports useful in-memory data structures. It can also handle session storage, rate limiting, leaderboards, lightweight pub/sub, and counters when those needs fit in memory.
Redis is useful because it gives you more than string keys and values:
Each data structure solves specific problems elegantly:
Redis also supports persistence (RDB snapshots, append-only log), replication (master-replica), and clustering (sharding across nodes). It's not just a cache; it can be your primary data store for the right use cases.
Saying "use Redis" is not enough. Tie it to the access pattern: "I'd use a Redis sorted set for the leaderboard because we need ordered score ranges and top-N queries."
Memcached is the simpler, older alternative to Redis. It does one thing: fast key-value caching. No data structures, no persistence, no pub/sub. Just keys and values with low latency.
Redis is usually easier to justify because it covers more use cases, but Memcached still has a place when you only need a simple cache:
| Aspect | Redis | Memcached |
|---|---|---|
| Data structures | Rich (lists, sets, hashes, sorted sets) | Key-value only |
| Persistence | Yes (RDB, AOF) | No |
| Replication | Yes | No |
| Clustering | Yes | Client-side sharding |
| Threading | Single-threaded core | Multi-threaded |
| Memory overhead | Higher | Lower |
| Use cases | Caching + more | Pure caching |
Practical recommendation: Start with Redis unless the requirement is only key-value caching and you have a specific performance or memory-efficiency reason to prefer Memcached.
This category often confuses candidates because there are two fundamentally different models: message queues (work distribution) and event streaming (event logs). Kafka and RabbitMQ look similar at first glance, but they solve different problems.
Kafka isn't a traditional message queue. It's a distributed commit log. Messages are written to an append-only log, and consumers read from that log at their own pace. Messages aren't deleted when consumed; they stay in the log until they age out.
This log-based model changes what the system can support:
Topics are divided into partitions for parallelism. Each partition is an ordered log. Within a consumer group, each partition is consumed by exactly one consumer, allowing parallel processing while maintaining order within a partition.
Mention Kafka when replay, ordering within partitions, high-throughput ingestion, or multiple independent consumers matter. Be ready to explain partitions and consumer groups.
RabbitMQ is a traditional message broker. Unlike Kafka's log-based model, RabbitMQ is designed for task distribution: send a message, exactly one consumer processes it, message is deleted.
RabbitMQ excels at work distribution. You have tasks (resize images, send emails, process payments), and you want to distribute them across workers. RabbitMQ handles the queuing, acknowledgments, retries, and dead-letter handling.
It also supports sophisticated routing. With exchanges and bindings, you can route messages based on patterns, headers, or topics. Need to send order messages to both the billing queue and the shipping queue? RabbitMQ handles that elegantly.
| Aspect | RabbitMQ | Kafka |
|---|---|---|
| Mental model | Smart broker, simple consumers | Dumb broker, smart consumers |
| Message fate | Deleted after acknowledgment | Retained in log |
| Multiple consumers | Competing (one wins) | Each group gets all messages |
| Ordering | Per queue | Per partition |
| Replay | No | Yes |
| Routing | Rich (exchanges, bindings) | Simple (topics) |
| Best for | Task queues, RPC | Event streaming, logs |
A useful rule: If you need work distribution (one message = one task for one worker), consider RabbitMQ. If you need event streaming (one event potentially processed by many systems), consider Kafka.
SQS is AWS's managed queue service. It is a practical choice when you are already on AWS and need a reliable queue without operating brokers.
You do not manage servers or broker clusters. You create a queue, send messages, receive messages, and AWS handles availability, durability, and scaling.
SQS offers two queue types:
| Need | Technology |
|---|---|
| Event streaming, replay, multiple consumers | Kafka |
| Task distribution, complex routing | RabbitMQ |
| Simple queuing on AWS, managed service | SQS |
| Simple queuing on GCP | Cloud Pub/Sub |
When someone types "blue running shoes size 10" into a search box, your relational database isn't going to cut it. Full-text search requires specialized technology that understands relevance, typos, synonyms, and ranking.
Elasticsearch is a common choice for full-text search and log analytics. Built on Apache Lucene, it provides relevance scoring, inverted indexes, aggregations, and a query language exposed over HTTP.
Your PostgreSQL database can do basic search with LIKE queries, but it struggles with:
Elasticsearch handles all of this natively.
The typical pattern: Your primary database (PostgreSQL, MongoDB) is the source of truth. You sync data to Elasticsearch for search. Users search against Elasticsearch, then fetch full records from the primary database.
Elasticsearch is not usually your system of record. It is optimized for search and analytics, not transactional integrity, relational constraints, or serving as the authoritative source for critical writes. Use it alongside a primary data store.
It also has a learning curve. The query DSL is expressive but complex. Indexing strategies, mapping types, and shard configuration require thought.
Mention Elasticsearch when the requirements include relevance ranking, fuzzy matching, faceting, autocomplete, or log search. Also say how it stays in sync with the primary database.
Your database stores structured data. But what about profile pictures, uploaded documents, video files, and backups? That's where object storage comes in.
S3 is the common AWS answer for object storage: uploaded files, media, backups, static assets, and data-lake objects.
S3 offers effectively unlimited object storage and very high durability. In interviews, the exact durability math matters less than the role: object storage is built for durable, cheap, large-scale blob storage rather than low-latency relational queries.
Not all data is accessed equally. S3 offers storage classes that trade access speed for cost:
| Storage Class | Access Pattern | Relative Cost |
|---|---|---|
| Standard | Frequent access | $$$$ |
| Intelligent-Tiering | Unknown/changing patterns | $$$ (auto-moves) |
| Standard-IA | Infrequent access | $$ |
| Glacier Instant | Archive, instant retrieval | $ |
| Glacier Deep Archive | Archive, hours to retrieve | ¢ |
Lifecycle policies automate transitions. For example: after 30 days, move to IA. After 90 days, move to Glacier. After 7 years, delete.
Use object storage for files and media, then mention CDN integration for user-facing content and lifecycle policies for cost optimization.
HDFS is specialized for big data. If you're processing terabytes or petabytes with Hadoop, Spark, or similar frameworks, HDFS is purpose-built for that.
Unlike S3-style object storage, HDFS behaves like a distributed filesystem for batch processing workloads. It fits best when your processing framework expects locality-aware file access.
When to mention HDFS in interviews: legacy Hadoop environments, batch processing, and data platforms that already depend on HDFS. For general cloud file storage, object storage is usually simpler.
When you have multiple servers, you need something to distribute traffic between them. That's your load balancer. But there's more to the choice than just "use a load balancer."
The key distinction is what layer of the network stack the load balancer understands:
Layer 4 (NLB, HAProxy in TCP mode): Routes based on IP address and port. Doesn't understand HTTP. Extremely fast, handles millions of connections. Use for non-HTTP protocols (databases, game servers) or when you need maximum performance.
Layer 7 (ALB, Nginx, HAProxy in HTTP mode): Understands HTTP. Can route based on URL paths, headers, or cookies. Can do SSL termination, add headers, rewrite URLs. More flexible but slightly slower.
Nginx is a common choice when you need an HTTP server, reverse proxy, or software load balancer that you can run and configure yourself.
For simpler self-managed setups, Nginx is often enough. In cloud-native environments, a managed load balancer or ingress controller may be simpler operationally.
If you're on AWS, GCP, or Azure, managed load balancers reduce operational work:
| Cloud | Layer 7 | Layer 4 |
|---|---|---|
| AWS | ALB (Application LB) | NLB (Network LB) |
| GCP | HTTP(S) Load Balancing | TCP/UDP Load Balancing |
| Azure | Application Gateway | Azure Load Balancer |
A CDN caches your content on servers distributed globally. Instead of every user hitting your origin in Virginia, users hit the nearest edge location, which might be in their city.
The speed of light is fixed. A user in Tokyo requesting data from a server in New York has a minimum ~100ms network latency. There's no software optimization that can fix physics.
A CDN puts cached copies of your content on edge servers worldwide. Now that Tokyo user hits a Tokyo edge server with ~10ms latency.
CloudFront is AWS's CDN. If you're on AWS, it integrates seamlessly with S3, ALB, and other AWS services. Lambda@Edge lets you run code at edge locations.
Cloudflare is a CDN-plus-more. Beyond caching, it provides DDoS protection, a Web Application Firewall (WAF), DNS services, and Workers for edge computing. Many companies use Cloudflare even if they're on AWS.
Mention a CDN when global users fetch cacheable content. Then explain what is cacheable, how invalidation works, and what should stay at the origin.
Containers have become the standard way to package and deploy applications. But running containers at scale requires orchestration: scheduling, scaling, healing, and networking. That's where Kubernetes comes in.
Docker packages your application and its dependencies into a container, which runs consistently across environments. No more "it works on my machine" problems.
You don't need deep Docker knowledge for system design interviews, but understand the basics:
Kubernetes (K8s) manages containerized applications across a cluster of machines. It handles the operational concerns that become critical at scale.
You don't need to know Kubernetes internals for most system design interviews. Knowing that "we'd deploy this on Kubernetes for scaling, self-healing, and zero-downtime deployments" is usually enough. Go deeper only if specifically asked.
You can't fix what you can't see. In production, observability is how you understand what your system is doing: is it healthy, is it fast enough, and when something breaks, why?
Modern observability combines three types of data:
This combination is the standard for metrics in Kubernetes environments.
Prometheus collects and stores time-series metrics. It pulls metrics from your services (they expose an endpoint), stores them efficiently, and provides PromQL for analysis and alerting.
Grafana visualizes those metrics as dashboards. It connects to Prometheus (and many other data sources) and lets you build dashboards that show system health at a glance.
This stack is open-source, widely used, and fits Kubernetes environments well. Many Kubernetes setups already include Prometheus-compatible metrics.
Datadog, New Relic, and Splunk are commercial platforms that provide metrics, logs, and traces in one package. You pay more, but you get a unified experience and less operational burden.
CloudWatch (AWS) is the built-in monitoring option for AWS services. It is integrated, but teams often add specialized observability tools as systems grow.
Mention monitoring as part of your design. "We'd expose Prometheus metrics from each service and set up Grafana dashboards for request latency, error rates, and resource utilization. Alerts would fire if p99 latency exceeds 500ms." This shows operational maturity.
An API gateway is the front door to your microservices. It's the single entry point that handles cross-cutting concerns so your services don't have to.
Instead of each service implementing authentication, rate limiting, and logging, the gateway handles it centrally:
/users/* to the user service, /orders/* to the order serviceAWS API Gateway is fully managed and integrates with Lambda and other AWS services. Good for serverless architectures.
Kong is open-source with an enterprise version. Feature-rich, runs anywhere.
Ambassador/Emissary is Kubernetes-native, built on Envoy.
For simpler cases, Nginx or your cloud's load balancer might be enough.
In microservices designs, mention an API gateway for handling authentication and rate limiting centrally, rather than implementing these in every service.
Choosing technologies isn't about memorizing which is "best." It's about matching tools to requirements. Here's a mental framework for making those decisions in interviews.
Before naming a technology, ask:
When you're in an interview and need to quickly recall which technology to mention:
| Problem | Go-to Choice | Alternative |
|---|---|---|
| Relational data, transactions | PostgreSQL | MySQL |
| Document data, flexible schema | MongoDB | DynamoDB |
| Massive writes, time-series | Cassandra | TimescaleDB |
| Caching, leaderboards, sessions | Redis | Memcached |
| Task queue, work distribution | SQS / RabbitMQ | Redis (simple cases) |
| Event streaming, logs | Kafka | Pulsar, Kinesis |
| Full-text search | Elasticsearch | Algolia (managed) |
| File storage | S3 | GCS, Azure Blob |
| Load balancing (L7) | ALB / Nginx | HAProxy |
| CDN | CloudFront / Cloudflare | Akamai |
| Container orchestration | Kubernetes | ECS, Cloud Run |
| Metrics + Visualization | Prometheus + Grafana | Datadog |
Many designs start with PostgreSQL for relational data, Redis for caching, Kafka for event streaming, Elasticsearch for search, and S3 for files. Treat these as defaults to justify, not names to memorize.
There's no universally "best" database or queue. Every choice involves tradeoffs. A strong answer shows that you understand what you're trading away, not just what you're getting.
Go deep on a few, wide on the rest. You can't be an expert in everything. Pick 2-3 technologies you know deeply (probably PostgreSQL, Redis, and Kafka or RabbitMQ). For the rest, know enough to choose appropriately and explain why.
"I'd use Redis" is weak. "I'd use Redis because we need sub-millisecond latency for session lookups, and sorted sets fit the leaderboard access pattern" shows understanding.
A technology can fit the model and still be operationally expensive. Managed services trade cost and lock-in for reduced operational burden. That's often a good trade.
Interview answers should reflect what you'd do in production. Don't over-engineer. Start with the simplest technology that meets requirements. You can explain when you'd scale up if requirements change.
20 quizzes