Skip to content
RED SAGSkills

System Design interview questions and answers

System design interviews ask you to design a service and defend the trade-offs. These questions cover the building blocks you need to reason about scale, consistency and failure.

Below are 60 System Design questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What System Design interviews usually check

  • ✓Estimating load, storage and bandwidth
  • ✓Caching, CDNs and load balancing
  • ✓Database choice, sharding and replication
  • ✓Queues, async processing and idempotency
  • ✓Consistency, availability and failure handling

System Design interview questions for freshers easy

1. Duplicate emails despite a check

Signup code runs SELECT to check whether the email exists and then INSERTs the user. Duplicate accounts still appear under heavy traffic. What is the right fix?

  1. A.Add a short sleep between the check and the insert
  2. B.Add a UNIQUE constraint (or unique index) on email and handle the duplicate-key error
  3. C.Run the SELECT check twice before inserting
  4. D.Wrap the SELECT and INSERT in one READ COMMITTED transaction so the check and the insert happen together
Show answer

Answer: B. Add a UNIQUE constraint (or unique index) on email and handle the duplicate-key error

Two requests can both pass the SELECT before either inserts, which is a race condition. Only the database can enforce uniqueness atomically, so a UNIQUE constraint makes the second insert fail and you return a 409 or a friendly error. Normalise case, for example a unique index on lower(email), so "A@x.com" and "a@x.com" clash.

2. Updating a counter in MongoDB

Many requests add 1 to a post's likes at the same time. Which update is safe?

  1. A.Read the post, add 1 in code, then save it
  2. B.updateOne({ _id }, { $inc: { likes: 1 } })
  3. C.updateOne({ _id }, { $set: { likes: oldLikes + 1 } })
  4. D.replaceOne({ _id }, { likes: oldLikes + 1 })
Show answer

Answer: B. updateOne({ _id }, { $inc: { likes: 1 } })

$inc changes the value inside the database in one atomic operation on that document, so no increment is lost. Reading and writing back from the app lets two requests start from the same old value. replaceOne would also wipe the other fields.

3. Counting API calls in Redis

Ten servers count API calls per customer in Redis. Which command keeps the count exact under concurrency?

  1. A.GET then SET with the value plus one
  2. B.APPEND calls:cust42 1
  3. C.INCR calls:cust42
  4. D.SETNX calls:cust42 1
Show answer

Answer: C. INCR calls:cust42

Redis runs each command one at a time, and INCR reads and updates the value in a single step, so concurrent increments are never lost. GET followed by SET from the app has a race window. INCR also creates the key at 0 if it does not exist.

4. What a load balancer does

An app runs on four identical servers behind a load balancer. What is the load balancer's main job?

  1. A.Spread requests across healthy servers
  2. B.Store session data for all four servers
  3. C.Cache database queries for the servers
  4. D.Encrypt traffic between the servers and the DB
Show answer

Answer: A. Spread requests across healthy servers

A load balancer sends each incoming request to one of the backend servers and stops sending to servers that fail health checks. That spreads load and hides single server failures. Session storage, caching and database encryption are separate concerns.

5. Latency versus throughput

Which statement correctly separates latency from throughput?

  1. A.Latency is requests per second; throughput is time per request
  2. B.Latency is time per request; throughput is requests per second
  3. C.Both mean the same thing measured in different units
  4. D.Latency only applies to databases, throughput to APIs
Show answer

Answer: B. Latency is time per request; throughput is requests per second

Latency measures how long one operation takes, often reported as percentiles like p50 and p99. Throughput measures how many operations complete per unit time. A system can have high throughput and still poor latency, for example when requests queue up.

6. Estimating requests per second

An app has 10 million daily active users, and each makes about 20 API requests per day. What is the average request rate?

  1. A.About 2,300 requests per second
  2. B.About 23,000 requests per second
  3. C.About 230 requests per second
  4. D.About 200,000 requests per second
Show answer

Answer: A. About 2,300 requests per second

10,000,000 x 20 = 200 million requests per day. A day has 86,400 seconds, and 200,000,000 / 86,400 is about 2,315 per second. Peaks are often 2 to 5 times the average, so you would plan for more.

7. How a token bucket limits rate

A token bucket rate limiter has capacity 10 and refills 1 token per second. A client has been idle for a minute and then sends 15 requests at once. How many are allowed immediately?

  1. A.15
  2. B.1
  3. C.10
  4. D.0
Show answer

Answer: C. 10

Tokens accumulate up to the bucket capacity, so after a long idle period the bucket holds 10. Each request takes one token, so 10 pass at once and 5 are rejected (or delayed). After that, about one request per second is allowed as tokens refill.

8. Large file uploads

Users upload videos up to 2 GB. Sending them through the API servers ties up memory and bandwidth. What is the common design?

  1. A.Give clients a pre-signed URL to upload to object storage
  2. B.Store each video as a BLOB column in the main database
  3. C.Raise the API servers’ request body limit to 2 GB
  4. D.Split each video into chunks and store them in Redis
Show answer

Answer: A. Give clients a pre-signed URL to upload to object storage

A pre-signed URL lets the client upload directly to S3 or similar for a short time with limited permission, so API servers only issue the URL and record metadata. Object storage is built for large files; databases and caches are not. Multipart uploads handle resumes for very large files.

9. Spotting a single point of failure

A system has 6 app servers across two zones, one load balancer pair, and one PostgreSQL server with nightly backups. What is the single point of failure?

  1. A.The load balancer pair
  2. B.The six app servers
  3. C.The two availability zones
  4. D.The single PostgreSQL server
Show answer

Answer: D. The single PostgreSQL server

A single point of failure is a component whose failure stops the whole system. If the only database server dies, every app server fails, and backups mean restoring and losing up to a day of data. A standby replica with automatic failover removes it.

10. DNS failover is slow

A team fails over to a backup region by changing a DNS record with a TTL of 3600 seconds. Why do many users still hit the dead region for a while?

  1. A.DNS changes need a full day to be approved
  2. B.Load balancers ignore DNS records completely
  3. C.Browsers only read DNS on the first ever visit
  4. D.Resolvers and clients cache the old record up to an hour
Show answer

Answer: D. Resolvers and clients cache the old record up to an hour

The TTL tells resolvers how long they may cache an answer, so clients can keep using the old IP for up to an hour (some cache even longer). Use low TTLs (for example 60 seconds) on records used for failover, or use anycast or a global load balancer.

11. Pushing live order status

A food app shows live order status that only the server updates. The browser never sends data on this channel. What is a simple fit?

  1. A.A WebSocket with a custom binary protocol
  2. B.Server-Sent Events over HTTP
  3. C.Polling every 100 milliseconds
  4. D.Email notifications for each status change
Show answer

Answer: B. Server-Sent Events over HTTP

Server-Sent Events give a one-way stream from server to browser over plain HTTP, with automatic reconnect. WebSockets are bidirectional and add more complexity than needed here. Very fast polling wastes requests and battery.

12. Getting a repository into a ViewModel

With Hilt, how do you get an OrdersRepository into OrdersViewModel?

  1. A.Annotate with @HiltViewModel and use an @Inject constructor
  2. B.Create the repository inside onCreate of the Activity
  3. C.Use a global lateinit repository set at startup
  4. D.Pass the repository through an Intent extra
Show answer

Answer: A. Annotate with @HiltViewModel and use an @Inject constructor

@HiltViewModel plus @Inject constructor(private val repo: OrdersRepository) lets Hilt build the ViewModel with its dependencies. In Compose, hiltViewModel() obtains it scoped to the navigation entry or activity.

13. Picking a framework for a React team

A startup's engineers all build React web apps with TypeScript. They need one mobile app for Android and iOS soon. Which choice usually fits best?

  1. A.React Native, so they reuse React, TypeScript and many npm libraries
  2. B.Flutter, because it shares more code with a React web codebase
  3. C.Two fully native apps in Swift and Kotlin written from scratch
  4. D.A PWA only, because app stores do not accept hybrid apps
Show answer

Answer: A. React Native, so they reuse React, TypeScript and many npm libraries

React Native lets the team keep the React mental model, hooks, TypeScript and much of their tooling and business logic. Flutter is a strong option too, but it needs the team to learn Dart and a different widget model. The best choice often depends on existing skills more than on raw benchmarks.

14. How often Ethereum makes a block

Under proof of stake, how often does Ethereum mainnet have a slot in which a new block can be proposed?

  1. A.Every 12 seconds
  2. B.Every 10 minutes
  3. C.Every 1 second
  4. D.Every 2 minutes
Show answer

Answer: A. Every 12 seconds

Ethereum time is split into 12-second slots, with 32 slots in an epoch (6.4 minutes). One validator is chosen to propose a block each slot, though a slot can be missed. Ten minutes is Bitcoin's average block time.

15. Why Ethereum moved to proof of stake

What was the main practical change when Ethereum switched from proof of work to proof of stake in 2022?

  1. A.Transaction fees went to zero
  2. B.Every transaction became private
  3. C.Energy use dropped by over 99% since mining was no longer needed
  4. D.Blocks started to need approval from a central company
Show answer

Answer: C. Energy use dropped by over 99% since mining was no longer needed

The Merge replaced energy-hungry mining with validators who lock up ETH. Energy use fell by roughly 99.95%. Fees and throughput did not change much; scaling is handled by layer 2 rollups.

16. Heavy work in an interrupt

A UART receive ISR parses a full command and writes to flash. What is the usual better design?

  1. A.Raise the ISR to the lowest priority
  2. B.Add more delay loops inside the ISR
  3. C.Store the byte, set a flag, and parse in the main loop
  4. D.Disable all interrupts for the whole program
Show answer

Answer: C. Store the byte, set a flag, and parse in the main loop

Long ISRs block other interrupts and raise latency, and slow work like flash writes can miss incoming bytes. A short ISR that buffers data and signals a task or the main loop keeps the system responsive.

17. printf in an interrupt

Why is calling printf inside an ISR a bad idea?

  1. A.printf only works before main has started
  2. B.The compiler removes printf from ISRs
  3. C.It is slow, may block, and is often not reentrant
  4. D.printf cannot print integers on ARM
Show answer

Answer: C. It is slow, may block, and is often not reentrant

printf formats text and may wait for a UART, taking milliseconds. Library versions may use locks or heap memory, so calling it from an ISR that interrupted another printf can corrupt state or deadlock. Log to a buffer instead.

18. ACID vs eventual consistency

A team compares a relational database with a store that offers only eventual consistency across replicas. What does eventual consistency mean?

  1. A.Every read always sees the latest write
  2. B.Writes are rejected until all replicas agree
  3. C.Data is checked for constraints once a day
  4. D.Replicas converge if writes stop, but reads may be stale
Show answer

Answer: D. Replicas converge if writes stop, but reads may be stale

With eventual consistency, a write is applied to replicas over time, so a read soon after may return older data. If no new writes happen, all replicas end up the same. ACID databases give stronger guarantees within one node or cluster, at some cost to availability or latency.

19. A logo in a browser tab

The full logo with name and tagline is unreadable as a 16×16 or 32×32 browser favicon. What is the usual approach?

  1. A.Use the simplified symbol or a single letter
  2. B.Shrink the full logo and hope it reads
  3. C.Use a photo of the founder
  4. D.Leave the favicon blank
Show answer

Answer: A. Use the simplified symbol or a single letter

At favicon sizes only a bold, simple shape survives. Brands use the symbol, a monogram or a cropped element, often redrawn with thicker strokes for small pixel sizes.

20. Paying reverse charge tax

A company owes ₹18,000 GST under reverse charge on legal fees. Can it pay this from its ITC balance?

  1. A.Yes, any ITC can pay reverse charge tax
  2. B.No, reverse charge tax must be paid in cash
  3. C.Yes, but only IGST credit can be used
  4. D.No, reverse charge tax is paid by the lawyer
Show answer

Answer: B. No, reverse charge tax must be paid in cash

Tax under reverse charge is not "output tax" for using credit, so it must be paid through the cash ledger. After paying it, the company can usually claim the same amount as ITC if the service is used for taxable business.

System Design intermediate interview questions medium

21. Async callback inside forEach

getPrice(id) returns a promise that resolves to 10 after a short delay. What does this print?

async function totalPrice(ids) {
  let total = 0
  ids.forEach(async (id) => {
    total += await getPrice(id)
  })
  return total
}

totalPrice([1, 2, 3]).then(console.log)
  1. A.30
  2. B.NaN
  3. C.0
  4. D.undefined
Show answer

Answer: C. 0

forEach ignores the promises returned by async callbacks, so totalPrice returns before any getPrice call has finished and logs 0. Use a for...of loop with await, or await Promise.all(ids.map(getPrice)) and sum the results. Linters such as typescript-eslint's no-misused-promises catch this.

22. Different prices on different servers

Each of 6 API servers caches prices in a local in-memory Map for 10 minutes. After a price change, users see different prices on refresh. Why?

  1. A.In-memory Maps lose their data every few seconds
  2. B.The load balancer rewrites the prices it sends
  3. C.JavaScript Maps are unable to store numbers
  4. D.Each server has its own copy, cleared separately
Show answer

Answer: D. Each server has its own copy, cleared separately

A local cache is fast but each process holds its own copy, so an update only clears the cache on the server that handled it. Use a shared cache like Redis, broadcast invalidation messages to all servers, or keep local TTLs very short.

23. Average latency with a cache

A cache answers in 1 ms and the database in 20 ms. On a miss the app checks the cache, then the database. With a 90% hit ratio, what is the average read latency?

  1. A.3 ms
  2. B.2.9 ms
  3. C.2 ms
  4. D.10.5 ms
Show answer

Answer: A. 3 ms

Hits cost 1 ms; misses cost 1 ms for the cache check plus 20 ms for the database, 21 ms. Average = 0.9 x 1 + 0.1 x 21 = 0.9 + 2.1 = 3.0 ms. Forgetting the cache check on a miss gives 2.9 ms. Raising the hit ratio to 99% brings it down to about 1.2 ms.

24. How many short URLs fit

A URL shortener uses 7-character codes from [a-zA-Z0-9]. Roughly how many unique codes exist?

  1. A.About 78 billion
  2. B.About 3.5 trillion
  3. C.About 10 million
  4. D.About 8 billion
Show answer

Answer: B. About 3.5 trillion

There are 62 characters, so 62^7 is about 3.52 x 10^12, or 3.5 trillion. Six characters give about 57 billion. Codes are often generated from a counter or ID converted to base62, which avoids collisions without checking.

25. Why look at p99 latency

Average API latency is 80 ms but users complain the app is often slow. What metric is most likely to show the problem?

  1. A.p99 latency
  2. B.Mean latency over a day
  3. C.Total requests per day
  4. D.Median CPU usage
Show answer

Answer: A. p99 latency

Averages hide the slow tail. If 1 in 100 requests takes 3 seconds, the mean barely moves but p99 shows it, and a page that makes 20 calls will often hit at least one slow one. Track p50, p95 and p99 and set SLOs on them.

26. IP rate limits block real users

A login rate limit of 10 requests per minute per IP keeps blocking many genuine users on one Indian mobile carrier. What is the likely cause?

  1. A.Mobile phones send requests twice by default
  2. B.The carrier caches login responses
  3. C.Carrier-grade NAT puts many users behind one IP
  4. D.IPv4 addresses change every minute
Show answer

Answer: C. Carrier-grade NAT puts many users behind one IP

Mobile carriers commonly use carrier-grade NAT, so thousands of users can share one public IPv4 address. Limiting purely by IP punishes all of them. Combine keys like account, device and IP, use higher per-IP limits, and add CAPTCHA or backoff for suspicious patterns.

27. Separate models for reads and writes

An order system has complex write rules, while the dashboard needs fast, heavily joined views. The team adopts CQRS. What does that mean in practice?

  1. A.Every request is written to two databases at once
  2. B.Writes use one model; reads use a separate, denormalised view
  3. C.Queries and commands share one table with more indexes
  4. D.Reads are served only from the write database
Show answer

Answer: B. Writes use one model; reads use a separate, denormalised view

CQRS (Command Query Responsibility Segregation) separates the model that handles changes from the models used for queries. The read side is often a denormalised store updated from events, so it is fast but eventually consistent. It adds moving parts, so use it where read and write needs really differ.

28. Surviving screen rotation

On Android, a screen loses its loaded data when the phone is rotated. What is the recommended fix?

  1. A.Lock the screen to portrait for all users
  2. B.Store the data in a Jetpack ViewModel, which survives configuration changes
  3. C.Store the data in a static global variable
  4. D.Reload the data in onResume every time
Show answer

Answer: B. Store the data in a Jetpack ViewModel, which survives configuration changes

Rotation is a configuration change that recreates the Activity by default. A ViewModel is retained across configuration changes, so its data stays. For process death you also need SavedStateHandle or persistent storage. Locking orientation hides the bug and hurts tablets and foldables.

29. Downloading a list of images

You need to download a variable number of thumbnails concurrently and collect the results. What fits best?

  1. A.One async let per URL written by hand
  2. B.withThrowingTaskGroup, adding one child task per URL
  3. C.A for loop that awaits each download in turn
  4. D.DispatchQueue.main.sync for each download
Show answer

Answer: B. withThrowingTaskGroup, adding one child task per URL

async let suits a fixed number of tasks. Task groups add any number of child tasks at runtime, run them concurrently, and let you collect results with for try await. Cancellation and errors propagate within the group.

30. Messy player code

Player code is full of flags like isJumping, isAttacking and isClimbing, causing bugs where two states run at once. What is a good refactor?

  1. A.Add more boolean flags to handle each combination
  2. B.Use a finite state machine where the player is in exactly one state with clear transitions
  3. C.Move all logic into one big Update method
  4. D.Run each state on a separate thread
Show answer

Answer: B. Use a finite state machine where the player is in exactly one state with clear transitions

A finite state machine makes the player be in one state at a time, such as Idle, Run, Jump or Climb. Each state has its own enter, update and exit logic, and only allowed transitions can happen. This removes impossible flag combinations and is easy to extend.

31. High-priority task starves

A low-priority logger holds a lock that a high-priority control task needs. A medium-priority task then runs for 200 ms, and the control task misses deadlines. What fixes this?

  1. A.Use a mutex with priority inheritance for the shared lock
  2. B.Give the medium task an even higher priority
  3. C.Replace the lock with a binary semaphore
  4. D.Make the logger task run more often
Show answer

Answer: A. Use a mutex with priority inheritance for the shared lock

This is priority inversion: the medium task preempts the lock holder, so the high task waits on both. Priority inheritance temporarily lifts the logger to the high task's priority while it holds the mutex, so it finishes and releases quickly. A plain semaphore has no owner and gives no inheritance.

32. Two tasks stop forever

Task A takes the SPI mutex then the flash mutex. Task B takes the flash mutex then the SPI mutex. Both freeze after some hours. What is the cleanest fix?

  1. A.Give task A a higher priority than task B
  2. B.Add a 1 ms delay between taking the two mutexes
  3. C.Use recursive mutexes for both resources
  4. D.Make every task take the two mutexes in the same order
Show answer

Answer: D. Make every task take the two mutexes in the same order

Each task holds one lock and waits for the other, which is a classic deadlock. A fixed global lock order makes the cycle impossible. Timeouts on taking locks help detect the problem, but consistent ordering removes it.

33. Gatekeeper task for a shared UART

Many tasks print to one UART. Instead of a mutex, a gatekeeper task owns the UART and others send it strings through a queue. What is a key benefit?

  1. A.Output can then go faster than the UART baud rate allows
  2. B.The queue removes the need for any RAM buffers at all
  3. C.Each writer task then gets its own physical UART port
  4. D.Writers do not block on the UART, and no inversion on it can occur
Show answer

Answer: D. Writers do not block on the UART, and no inversion on it can occur

Only the gatekeeper touches the UART, so no lock is needed and no task waits on a slow lock holder. Writers just queue a message and carry on, unless the queue is full. The cost is RAM for the queue and a little latency.

34. How much Lambda concurrency you need

A function receives 200 requests per second and each takes 500 ms. About how many concurrent executions does it need?

  1. A.About 200
  2. B.About 400
  3. C.About 40
  4. D.About 100
Show answer

Answer: D. About 100

Concurrency ≈ requests per second × duration in seconds = 200 × 0.5 = 100. Check this against your account's concurrency quota (1,000 by default in many regions) and any reserved concurrency settings.

35. Why add jitter to retries?

Your clients retry failed calls with exponential backoff. Why should you also add random jitter to the wait time?

  1. A.To stop many clients from retrying at the same moment and overloading the recovering service in synchronized waves
  2. B.To make each retry faster than the previous one
  3. C.To make sure every retry goes to a different region
  4. D.Jitter is only needed when the requests are not idempotent
Show answer

Answer: A. To stop many clients from retrying at the same moment and overloading the recovering service in synchronized waves

If many clients fail at the same time and use the same backoff schedule, they all retry together, causing waves of load (a thundering herd). Random jitter spreads the retries out over time. Retrying non-idempotent requests is a separate safety problem that jitter does not solve.

36. Timeouts in Go clients

In a Go service, what is the idiomatic way to stop an outbound HTTP call from waiting longer than 2 seconds and to cancel it if the incoming request is cancelled?

  1. A.Create the request with a context from context.WithTimeout(r.Context(), 2*time.Second)
  2. B.Call time.Sleep(2 * time.Second) before the request
  3. C.Use a global variable that is checked after the call returns
  4. D.Set GOMAXPROCS to 2
Show answer

Answer: A. Create the request with a context from context.WithTimeout(r.Context(), 2*time.Second)

Deriving a context from the incoming request with a timeout means the call stops after 2 seconds and is also cancelled if the caller goes away. http.NewRequestWithContext uses it for the whole call. Remember to call the cancel function, usually with defer.

37. Two clerks edit the same record

Two support agents open the same customer record and both save changes. The second save silently overwrites the first. Which model change helps detect this?

  1. A.A version column checked and incremented on update
  2. B.A created_at column on the table
  3. C.A UNIQUE constraint on the customer name
  4. D.A separate table for each agent
Show answer

Answer: A. A version column checked and incremented on update

With optimistic locking, each update says WHERE id = ? AND version = ?, and sets version = version + 1. If someone else saved first, no row matches and the app can warn the user. It avoids holding locks while a person edits.

38. Threads with nothing to do

A Kafka Streams app reads one 12-partition topic. You run 3 instances, each with num.stream.threads=8. How many threads actually process data?

  1. A.12, because there are only 12 tasks
  2. B.24, all threads share the work
  3. C.8, one instance does all the work
  4. D.3, one thread per instance
Show answer

Answer: A. 12, because there are only 12 tasks

Kafka Streams creates one task per input partition, so this app has 12 tasks. A task runs on one thread at a time, so 12 of the 24 threads get work and 12 sit idle. To use more threads, the input needs more partitions.

39. Punctuating bullet lists

A bulleted list has items that are short fragments, such as 'Free delivery' and 'Easy returns'. Some end in full stops and some do not. What is the best edit?

  1. A.Make them consistent; fragments usually take no full stop
  2. B.Add a full stop and 'and' to every item
  3. C.Add a semicolon to each item and a full stop to the last
  4. D.Leave it, since bullet punctuation does not matter
Show answer

Answer: A. Make them consistent; fragments usually take no full stop

Most modern styles leave short fragment bullets without end punctuation and use full stops when bullets are complete sentences. The key rule is consistency within a list. Semicolons and 'and' after each item are an older, heavier style.

40. Cash paid under the wrong head

A taxpayer mistakenly deposited ₹50,000 under CGST instead of IGST in its cash ledger. What can it do?

  1. A.Move it to IGST through Form PMT-09
  2. B.Nothing; it can only be refunded after a year
  3. C.Use the CGST cash to pay IGST directly
  4. D.Ask the bank to reverse the payment
Show answer

Answer: A. Move it to IGST through Form PMT-09

Form PMT-09 lets a taxpayer transfer amounts in the electronic cash ledger between major heads (IGST, CGST, SGST, cess) and minor heads (tax, interest, penalty, fee). It works on unused balances only, not tax already set off.

System Design interview questions for experienced candidates hard

41. Burst at a fixed window boundary

A fixed-window limiter allows 100 requests per calendar minute per client. What is the most requests a client can get accepted within a 2-second span?

  1. A.100
  2. B.101
  3. C.200
  4. D.6,000
Show answer

Answer: C. 200

The client can send 100 requests at 12:00:59 and another 100 at 12:01:00, because the counter resets at the boundary. That is double the intended rate in two seconds. Sliding window logs, sliding window counters or a token bucket smooth this out.

42. Pool exhausted during payments

Checkout opens a transaction, inserts the order, calls the payment provider over HTTP (2 to 5 seconds), then commits. With a pool of 20 connections, checkout times out at peak. What is the best redesign?

  1. A.Raise the pool size to 500 connections and set the database max_connections high enough to cover every single app instance
  2. B.Use READ UNCOMMITTED so the transaction finishes faster
  3. C.Wait forever for a free connection instead of timing out
  4. D.Commit the order as pending first, call the provider with an idempotency key, then update it in a new short transaction
Show answer

Answer: D. Commit the order as pending first, call the provider with an idempotency key, then update it in a new short transaction

Each checkout holds a connection, and row locks, for the whole HTTP call, so 20 slow payments block everyone. Short transactions around local work, plus a pending state and idempotency key, keep the pool free and make retries safe. A reconciliation job can finish orders left pending after a crash.

43. One company sees another's invoices

In a multi-tenant SaaS, a new report query forgot `WHERE tenant_id = ?`. Company A saw Company B's invoices. Which approach prevents this whole class of bug?

  1. A.Ask reviewers to look for tenant_id in PRs
  2. B.Give each tenant a different colour theme
  3. C.Hide each tenant's report URLs from others
  4. D.Enforce tenant scope centrally, e.g. with RLS
Show answer

Answer: D. Enforce tenant scope centrally, e.g. with RLS

Relying on every developer to remember a filter will fail eventually. Enforce scoping in one place: a repository layer that always adds the tenant, PostgreSQL row-level security driven by a session setting, or separate schemas or databases. Add tests that try to read across tenants.

44. Keeping OAuth tokens out of the browser

A security review says a single-page app should not hold OAuth refresh tokens in JavaScript at all. Which pattern does that?

  1. A.A backend-for-frontend holds tokens and gives the SPA a session cookie
  2. B.Store the refresh token in sessionStorage instead
  3. C.Encrypt the refresh token with a key in the JavaScript bundle
  4. D.Use the implicit flow so no refresh token is issued
Show answer

Answer: A. A backend-for-frontend holds tokens and gives the SPA a session cookie

In the BFF pattern, a small server-side component runs the OAuth flow, keeps the tokens and calls APIs for the SPA. The browser only has an HttpOnly, Secure, SameSite session cookie, so an XSS bug cannot read tokens. A key inside the bundle is visible to anyone.

45. CROSSSLOT errors after moving to Cluster

After moving to Redis Cluster, MGET cart:42 wishlist:42 fails with CROSSSLOT. What is the fix if both must be read together?

  1. A.Increase the number of hash slots
  2. B.Run the same command on a replica
  3. C.Use hash tags, like {user:42}, in both keys
  4. D.Rename both keys to use only digits
Show answer

Answer: C. Use hash tags, like {user:42}, in both keys

Redis Cluster spreads keys over 16,384 slots by hashing the key name, and multi-key commands only work when all keys are in the same slot. Only the part inside {} is hashed, so keys with the same tag land together. Do not put everything under one tag, or one node gets all the load.

46. One Redis key gets half of all traffic

During a cricket final, one key holding the live score gets 300,000 reads a second, and the Redis node holding it is maxed out while others idle. What helps?

  1. A.Move the key to a node with more disk
  2. B.A brief local cache per app, or key copies
  3. C.Give the key a longer name to spread it
  4. D.Delete the key straight after every read
Show answer

Answer: B. A brief local cache per app, or key copies

A hot key always maps to one node, so adding nodes does not spread its load. A short in-process cache of a second or two in each app server removes most reads, and copies such as score:{1..10} read at random spread the rest. Read replicas can also help.

47. Keeping wallet balances only in Redis

A team wants to keep wallet balances only in Redis, because it is fast. What is the main risk?

  1. A.Async replication and persistence gaps can lose data
  2. B.Redis cannot store decimal numbers in string values
  3. C.Redis allows only 1,000 keys in each logical database
  4. D.Redis does not support atomic integer increments
Show answer

Answer: A. Async replication and persistence gaps can lose data

Redis replication is asynchronous and persistence has loss windows, so a failover can lose acknowledged writes, and memory limits cap size. For money, keep the system of record in a durable, transactional database, and use Redis for caching, rate limits or fast counters you can rebuild.

48. A join across two services' databases

With database-per-service, the orders service needs customer names for its order list, but customers live in another service's database. What is a common solution?

  1. A.Query the other service's tables directly
  2. B.Merge both services under one shared database user
  3. C.Ask the frontend to join the data by hand
  4. D.Keep a local copy of customer names updated by events
Show answer

Answer: D. Keep a local copy of customer names updated by events

Reading another service's tables couples you to its schema. Instead, call its API (with batching) or keep a small local projection of the fields you need, updated from its events. The projection is eventually consistent, which is usually fine for display names.

49. Adding a cache server without reshuffling

Cache keys are assigned with server = hash(key) % N. Going from 4 to 5 servers moves most keys and causes a flood of cache misses. What reduces this?

  1. A.A larger modulus such as % 1000
  2. B.Sorting keys before assignment
  3. C.Consistent hashing on a ring
  4. D.Using MD5 instead of the current hash
Show answer

Answer: C. Consistent hashing on a ring

With % N, changing N changes the bucket for most keys. Consistent hashing places servers and keys on a ring, so adding a server only takes over keys from its neighbour, about 1/N of the total. Virtual nodes smooth out the load.

50. What a Bloom filter can promise

A sign-up page checks a Bloom filter of taken usernames before querying the database. What can the filter say for sure?

  1. A."Taken" is reliable; "not taken" can sometimes be wrong
  2. B.Both answers are always exact, just faster than a DB
  3. C."Definitely not taken" is reliable; "maybe taken" can be wrong
  4. D.It can list every taken username on request
Show answer

Answer: C. "Definitely not taken" is reliable; "maybe taken" can be wrong

A Bloom filter sets several bits per item. If any bit for a name is unset, it was never added. If all are set, other names may have set them, so you confirm with the database. It uses far less memory than a set, and plain Bloom filters do not support deletes.

51. Stopping double charges on retries

A mobile app retries a UPI payment request when the network drops, and some customers are charged twice. What server-side fix uses a key-value store?

  1. A.Store each idempotency key with its result and replay it on repeats
  2. B.Hash the amount and reject any repeated amounts
  3. C.Keep a Set of customer IDs who paid today and block them
  4. D.Retry the bank call until it returns a success
Show answer

Answer: A. Store each idempotency key with its result and replay it on repeats

The client sends a unique idempotency key per payment attempt. The server records key → outcome (with a TTL); a retry with the same key gets the stored result instead of a new charge. The check-and-insert must be atomic, for example SET NX in Redis or a unique constraint.

52. One tenant floods the queue

A SaaS uses one job queue for all tenants. One customer uploads 2 million rows, and every other tenant's small jobs wait hours. What is a good fix?

  1. A.Isolate tenants so each gets a fair share of workers
  2. B.Add a single priority level for all small jobs
  3. C.Raise visibility timeout so big jobs finish first
  4. D.Move the large tenant's data to a bigger database
Show answer

Answer: A. Isolate tenants so each gets a fair share of workers

A shared FIFO-style queue lets one heavy tenant create head-of-line delay for everyone. Fair scheduling, such as per-tenant queues served round-robin, per-tenant concurrency limits, or SQS FIFO groups per tenant, keeps others moving. A generic priority flag does not stop the flood from filling the lanes.

53. One hot RabbitMQ queue

One RabbitMQ queue handles 40,000 messages per second and tops out, even though the cluster has idle nodes and CPU. Why, and what helps?

  1. A.The exchange is the bottleneck; switch to fanout
  2. B.A queue lives on one node and core; split it across queues
  3. C.Prefetch is too low; set it to unlimited
  4. D.The disk is too slow; turn off persistence
Show answer

Answer: B. A queue lives on one node and core; split it across queues

Each RabbitMQ queue is handled by a single Erlang process on one node, so one queue has a throughput ceiling no matter how big the cluster is. Spread load across several queues, for example with the consistent-hash exchange or sharding plugin, or use streams or super streams. Turning off persistence trades away safety.

54. A fixed concurrency limit

A service caps concurrent requests to its database at a fixed 200. On some days the database copes with 400; on others it slows down at 120. What approach handles both?

  1. A.A higher fixed limit of 1,000 requests
  2. B.Removing the limit and relying on timeouts
  3. C.A fixed limit of 120 for every single day
  4. D.A limit that adapts to observed latency
Show answer

Answer: D. A limit that adapts to observed latency

Adaptive concurrency limits (for example TCP-style AIMD or gradient algorithms, as in Netflix's concurrency-limits or Envoy) increase the limit while latency stays low and cut it when latency rises. They follow real capacity instead of a guess. Fixed limits either waste capacity or allow overload.

55. Fan-out for a celebrity account

A feed uses fan-out on write. A cricketer with 40 million followers posts, and feed writes back up for an hour. What design is common?

  1. A.Fan out to all 40 million followers in one transaction
  2. B.Fan out for normal users; merge celebrity posts at read time
  3. C.Stop showing celebrity posts in feeds
  4. D.Give the celebrity a separate database server
Show answer

Answer: B. Fan out for normal users; merge celebrity posts at read time

Hybrid feeds push posts from normal accounts to followers' feeds, but for accounts with huge follower counts they skip fan-out and fetch their recent posts when a follower opens the feed, then merge. This caps write amplification while keeping reads fast for most users.

56. Offline edits that must merge

A notes app lets users edit the same list offline on two phones and must merge changes without losing either side. What fits best?

  1. A.Last-write-wins on the whole document
  2. B.Locking the note on the first device to open it
  3. C.Rejecting the second phone's changes on sync
  4. D.A CRDT that merges concurrent edits deterministically
Show answer

Answer: D. A CRDT that merges concurrent edits deterministically

CRDTs (conflict-free replicated data types) are designed so replicas can change independently and always converge to the same state when merged, without coordination. Whole-document LWW drops one user's work, and locks do not work while offline. Libraries like Yjs and Automerge implement this.

57. Designing a wallet ledger

A payments team stores each wallet's balance as one mutable number. Audits cannot explain mismatches. What design is standard for money?

  1. A.An append-only double-entry ledger of postings
  2. B.A balance column updated with UPDATE statements
  3. C.A nightly batch that recalculates balances from logs
  4. D.A cache of balances refreshed every minute
Show answer

Answer: A. An append-only double-entry ledger of postings

In a double-entry ledger every transfer is recorded as immutable entries that debit one account and credit another, summing to zero. Balances are derived (and can be cached) from the entries, so every change is traceable and mistakes are fixed with new reversing entries, not edits.

58. Passing a class into a Task

In Swift 6 language mode, sending a mutable non-Sendable class instance from the main actor into a detached Task gives an error. Why?

  1. A.Both sides could mutate it at once, a data race
  2. B.Classes cannot be used inside any task at all
  3. C.Detached tasks only accept String values
  4. D.The class must first be marked with @objc
Show answer

Answer: A. Both sides could mutate it at once, a data race

Values crossing isolation boundaries must be Sendable, or proven safe to transfer. A mutable class shared by two concurrent contexts could race. Fixes include making it a struct, an actor, immutable and final Sendable, or not sharing it after sending.

59. Scaling stopped at a hidden limit

During a big sale, auto scaling stopped adding instances at 64 vCPUs although max size allowed more. What most likely happened, and how do you prevent it?

  1. A.Auto scaling is limited to 64 vCPUs for all users
  2. B.An account vCPU quota was hit; raise quotas ahead
  3. C.The load balancer refused more targets
  4. D.The AMI was too large to launch more copies
Show answer

Answer: B. An account vCPU quota was hit; raise quotas ahead

Accounts have service quotas, such as on-demand vCPUs per instance family per region. When reached, launches fail even if scaling allows more. Review quotas and request increases before planned peaks, and alarm on usage.

60. Two leaders after a GC pause

A worker holding a leader lease in etcd paused for 40 s in garbage collection. Its lease expired, a new leader was elected, and then the old worker woke up and wrote to storage, corrupting data. What prevents this?

  1. A.Make the lease longer than any possible pause
  2. B.Use fencing tokens: each new lease gets a higher number and storage rejects writes with an older token
  3. C.Check the lease once before starting the job
  4. D.Run the leader on a bigger machine
Show answer

Answer: B. Use fencing tokens: each new lease gets a higher number and storage rejects writes with an older token

A process cannot reliably know it has been paused, so checking the lease before writing still leaves a race. A fencing token, such as the etcd revision of the lease key, increases with every new leader, and the storage layer rejects writes carrying an older token. No lease length can rule out every pause.

Ready to test yourself?

Take a 15-question System Design MCQ test with a score at the end, or practise the full System Design question bank and earn points on the leaderboard.