Use this skill when designing distributed systems, architecting scalable services, preparing for system design interviews, or making infrastructure decisions. Triggers on load balancing, CAP theorem, sharding, replication, caching strategies, message queues, microservices architecture, database selection, rate limiting, and any task requiring high-level system architecture decisions.
Use this skill when designing distributed systems, architecting scalable services, preparing for system design interviews, or making infrastructure decisions. Triggers on load balancing, CAP theorem, sharding, replication, caching strategies, message queues, microservices architecture, database selection, rate limiting, and any task requiring high-level system architecture decisions.
When this skill is activated, always start your first response with the 🧢 emoji.
System Design
A practical framework for designing distributed systems and architecting scalable
services. This skill covers the core building blocks - load balancers, databases,
caches, queues, and CDNs - plus the trade-off reasoning required to use them well.
It is built around interview scenarios because they compress the full design process
into a repeatable structure you can also apply in real-world architecture decisions.
Agents can use this skill to work through any system design problem from capacity
estimation through detailed component design.
When to use this skill
Trigger this skill when the user:
Asks "how would you design X?" where X is a product or service
Needs to choose between SQL and NoSQL databases
Is evaluating load balancing, sharding, or replication strategies
Asks about the CAP theorem or consistency vs availability trade-offs
Is designing a caching strategy (what to cache, where, how to invalidate)
Needs to estimate traffic, storage, or bandwidth for a system
Is preparing for a system design interview
Asks about rate limiting, API gateways, or CDN placement
Do NOT trigger this skill for:
Line-level code review or specific algorithm implementations (use a coding skill)
DevOps/infrastructure provisioning details like Terraform or Kubernetes manifests
Key principles
Start simple and justify complexity - Design the simplest system that satisfies
the requirements. Introduce each new component (queue, cache, shard) only when you
can name the specific constraint it solves. Complexity is a cost, not a feature.
Network partitions will happen - choose C or A - CAP theorem says distributed
systems must sacrifice either consistency or availability during a partition. You
cannot avoid partitions (P is not a choice). Pick CP for financial and inventory
data; pick AP for feeds, caches, and preferences.
Scale horizontally, partition vertically - Stateless services scale out behind
a load balancer. Data scales by separating hot from cold paths: read replicas before
sharding, sharding before multi-region. Vertical scaling buys time; horizontal
scaling buys headroom.
Design for failure at every layer - Every service will go down. Every disk will
fill. Design fallback behavior before the happy path. Timeouts, retries with backoff,
circuit breakers, and bulkheads are not optional refinements - they are table stakes.
- A component that does two things will be
bad at both. Load balancers balance load. Caches serve reads. Queues decouple
producers from consumers. Mixing responsibilities creates invisible coupling that
makes the system fragile under load.
Single responsibility for components
Core concepts
System design assembles six core building blocks. Each solves a specific problem.
Load balancers distribute requests across backend instances. L4 balancers route by
TCP/IP; L7 balancers route by HTTP path, headers, and cookies. Use L7 for HTTP
services. Algorithms: round-robin (default), least-connections (when request latency
varies), consistent hashing (when you need sticky routing, e.g., cache affinity).
Caches reduce read latency and database load. Sit in front of the database.
Patterns: cache-aside (default), write-through (strong consistency), write-behind
(high write throughput, tolerate loss). Key concerns: TTL, invalidation strategy,
and stampede prevention. Redis is the default; Memcached only when pure key-value at
massive scale.
Databases are the source of truth. SQL for structured data with ACID transactions;
NoSQL for scale, flexible schemas, or specific access patterns. Read replicas for
read-heavy workloads. Sharding for write-heavy workloads that exceed one node.
Message queues decouple producers from consumers and absorb traffic spikes. Use
for async work, fan-out events, and unreliable downstream dependencies. Always
configure a dead-letter queue. SQS for AWS-native work; Kafka for high-throughput
event streaming or replay.
CDNs cache static assets and edge-terminate TLS close to users. Reduces origin
load and cuts latency for geographically distributed users. Use for images, JS/CSS,
and any content with high read-to-write ratio.
API gateways enforce cross-cutting concerns - auth, rate limiting, request logging,
TLS termination - at a single entry point. Never build a custom gateway; use Kong,
Envoy, or a managed provider.
Common tasks
Design a URL shortener
Clarifying questions: Read-heavy or write-heavy? Need analytics? Custom slugs?
Global or single-region?
Components:
API service (stateless, horizontally scaled) behind L7 load balancer
Key generation service - pre-generate Base62 short codes in batches and store
in a pool; avoids hot write path
Database - a relational DB works at moderate scale; switch to Cassandra for
multi-region or >100k writes/sec
Redirect flow: Client hits CDN -> cache hit returns 301/302 -> cache miss reads
DB -> populates cache -> returns redirect.
Scale signal: 100M URLs stored, 10B reads/day -> cache hit rate must be >99%
to protect the DB.
Design a rate limiter
Algorithm choices:
Token bucket (default) - allows bursts up to bucket capacity; fills at a
constant rate. Best for user-facing APIs.
Fixed window - simple counter per time window. Prone to burst at window edge.
Sliding window log - exact, but memory-intensive.
Sliding window counter - approximation using two fixed windows. Good balance.
Storage: Redis with atomic INCR and EXPIRE. Single Redis node is enough up to
~50k RPS per rule; use Redis Cluster for more.
Placement: In the API gateway (preferred) or as middleware. Always return
X-RateLimit-Remaining and Retry-After headers with 429 responses.
Distributed concern: With multiple gateway nodes, the counter must be centralized
(Redis) - local counters undercount.
Design a notification system
Components:
Notification API - accepts events from internal services
Router service - reads user preferences and determines channels (push, email, SMS)
Channel-specific workers (separate services) - dequeued from per-channel queues
Template service - renders notification copy
Delivery tracking - records sent/delivered/failed per notification
Queue design: One queue per channel (push-queue, email-queue, sms-queue).
Isolates failure - SMS provider outage does not back up email delivery.
Critical path vs non-critical path:
OTP and security alerts: synchronous, priority queue
Marketing and social notifications: async, best-effort, can be batched
Design a chat system
Protocol: WebSockets for real-time bidirectional messaging. Long-polling as
fallback for restrictive networks.
Storage split:
Message history: Cassandra, keyed by (channel_id, timestamp). Append-only,
high write throughput, easy time-range queries.
User presence and metadata: Redis (in-memory, fast reads).
User and channel info: PostgreSQL (relational, ACID).
Fanout: When a user sends a message, the server writes to the DB and then
publishes to a pub/sub channel (Redis Pub/Sub or Kafka). Each recipient's
connection server subscribes to relevant channels and pushes to the WebSocket.
Scale concern: Connection servers are stateful (WebSockets). Route users to
the same connection server with consistent hashing. Use a service mesh for
connection server discovery.
Choose between SQL vs NoSQL
Use this decision table:
Need
Choose
ACID transactions across multiple entities
SQL
Complex joins and ad-hoc queries
SQL
Strict schema with referential integrity
SQL
Horizontal write scaling beyond single node
NoSQL (Cassandra, DynamoDB)
Flexible or evolving schema
NoSQL (MongoDB, DynamoDB)
Graph traversals
Graph DB (Neo4j)
Time-series data at high ingestion rate
TimescaleDB or InfluxDB
Key-value at very high throughput
Redis or DynamoDB
Default: Start with PostgreSQL. It handles far more scale than most teams
expect and its JSONB column covers flexible-schema needs up to moderate scale.
Migrate to specialized stores when you have a measured bottleneck.
Estimate system capacity
Use the following rough constants in back-of-envelope estimates:
Sharding is complex and expensive; replication + caching handles most read bottlenecks
Add read replicas and caching first; only shard when writes are the bottleneck
Shared database between services
Creates hidden coupling; one service's slow query can kill another
One database per service; expose data through APIs or events
Cache without invalidation plan
Stale reads cause data inconsistency; cache-DB drift grows silently
Define TTL and invalidation triggers before adding any cache
Ignoring the tail: all QPS estimates as average
p99 latency matters more than p50; a 2x peak multiplier is the minimum
Always model peak QPS (2-3x average) and design capacity for it
Single point of failure at every layer
Load balancer with no standby, single queue broker, one region
Identify SPOFs explicitly; add redundancy for any component whose failure kills the system
Gotchas
CAP theorem is about partitions, not a free choice - You cannot "choose" to sacrifice partition tolerance. P is always present in distributed systems. The real choice is between C and A when a partition occurs. Framing it as a three-way trade-off is wrong.
Caching invalidation is the hard part, not caching itself - Most designs add Redis without defining when data becomes stale. The moment a cache-aside entry is written, define the exact condition that invalidates it. "We'll figure that out later" causes stale reads in production.
Read replicas have replication lag - Writes go to the primary; reads from replicas may be 10-100ms stale. If you route reads to replicas immediately after writes (e.g., "create, then fetch profile"), users will see the old version. Use read-after-write consistency or route critical reads to primary.
Consistent hashing does not eliminate hotspots - If one key receives dramatically more requests than others (celebrity user, viral post), consistent hashing still routes all requests for that key to the same shard. Solve with key-based sharding variants like adding a suffix, or cache at a higher layer.
Message queues do not guarantee exactly-once delivery - SQS standard queues deliver at-least-once; consumers must be idempotent. Kafka can deliver exactly-once within a single cluster but not across network boundaries. Design consumers to handle duplicate messages before relying on queue semantics.
References
For detailed frameworks and opinionated defaults, read the relevant file from the
references/ folder:
references/interview-framework.md - step-by-step interview process (RESHADED),
time allocation, common follow-up questions, and how to communicate trade-offs
Only load the references file when the task requires it - it is long and will
consume context.
Companion check
On first activation of this skill in a conversation: check which companion skills are installed by running ls ~/.claude/skills/ ~/.agent/skills/ ~/.agents/skills/ .claude/skills/ .agent/skills/ .agents/skills/ 2>/dev/null. Compare the results against the recommended_skills field in this file's frontmatter. For any that are missing, mention them once and offer to install: