All jobs / Full-stack developer
System Design Basics interview questions
System Design Basics questions a Full-stack developer can be asked, from first job to senior. Open one to see the strongest answer and why it works.
An interviewer says: “Design a URL shortener.” What do you do first?
The strongest answer: Ask what it must do and at what scale — reads vs writes, expiry, custom aliases, analytics — and state the assumptions you will design against before drawing anything
Why it works: Right. Requirements and numbers first; the boxes follow from them.
- Clarify functional and non-functional requirements first.
- State assumed scale in numbers.
- Only then draw.
- Check assumptions with the interviewer.
What does a load balancer do, and what does it not do?
The strongest answer: It spreads incoming requests across several identical servers and stops sending to ones that fail health checks; it does not make one slow server faster or fix a database bottleneck behind them
Why it works: Right. Distribution and failover, nothing more.
- Distributes requests across servers.
- Removes unhealthy servers.
- Not a cache, not capacity.
- Bottlenecks behind it remain.
What is the difference between scaling up and scaling out?
The strongest answer: Up means a bigger machine — simple, but there is a ceiling and one box to lose; out means more machines behind a balancer, which needs the service to be stateless or to share state elsewhere
Why it works: Right. Up is easy until the ceiling; out needs stateless services.
- Up: bigger box, ceiling, single failure.
- Out: more boxes, needs statelessness.
- Choose by state and ceiling.
- Neither is always right.
Why do interviewers keep asking whether a service is stateless?
The strongest answer: Because a stateless service keeps nothing between requests on its own box, so any instance can serve any request — that is what lets you add instances, replace a dead one and deploy without users noticing
Why it works: Right. Statelessness is what makes scaling out and failover trivial.
- Stateless: nothing kept between requests locally.
- Any instance serves any request.
- State moves to a shared store.
- Enables scale-out and failover.
When does adding a cache help, and when does it not?
The strongest answer: It helps when the same data is read far more often than it changes and a slightly stale answer is acceptable; it does not help write-heavy paths, unique-per-request data, or when staleness is unacceptable
Why it works: Right. Read-heavy, repeated, staleness-tolerant — otherwise it adds complexity for nothing.
- Read-heavy and repeated.
- Staleness must be acceptable.
- Not for writes or unique reads.
- Name what is cached and for how long.
In a design, why do you say which columns are indexed?
The strongest answer: Because the query pattern decides the index: a lookup by short code needs an index on that column or every read scans the table; each index also slows writes and takes space, so you index what you query, not everything
Why it works: Right. Index the lookup paths; pay on writes.
- Index what you look up by.
- Each index costs writes and space.
- Query pattern decides.
- Say the key columns explicitly.
What is a read replica for?
The strongest answer: A copy of the database that receives writes from the primary and serves reads, so read traffic scales out and the primary is left for writes; reads from it can lag slightly behind the primary
Why it works: Right. Reads scale out; expect replication lag.
- Copies primary; serves reads.
- Scales reads, not writes.
- Lag exists.
- Primary stays for writes.
Why would you put a queue between two parts of a system?
The strongest answer: To decouple them: the producer finishes fast and the consumer works at its own pace, spikes are absorbed, and a consumer being down does not lose the work — at the cost of the result arriving later
Why it works: Right. Decoupling and absorbing spikes, paid for in latency.
- Decouples producer and consumer.
- Absorbs spikes.
- Survives consumer outages.
- Result becomes asynchronous.
What is the difference between latency and throughput?
The strongest answer: Latency is how long one request takes; throughput is how many the system handles per second — you can have high throughput with bad latency, and adding servers raises throughput without shortening one request
Why it works: Right. One request's time versus requests per second.
- Latency: time per request.
- Throughput: requests per second.
- They move independently.
- Scaling out helps throughput.
An interviewer asks for p99 latency. What does that mean and why ask for it?
The strongest answer: The time under which 99% of requests finish — it shows the slow tail that averages hide, and the tail is what users on the worst path feel and what times out downstream
Why it works: Right. Tails matter more than averages.
- 99% of requests finish under it.
- Exposes the tail.
- Averages hide outliers.
- Tail drives user experience and timeouts.
How do you estimate storage for 100 million short URLs?
The strongest answer: Pick a per-record size — say a few hundred bytes for the code, the long URL and metadata — multiply, and round: a few hundred bytes times 100 million is tens of gigabytes, so one database handles it and the interesting problem is reads, not storage
Why it works: Right. Rough units, a multiplication, a conclusion.
- Per-record size times count.
- Round to a power of ten.
- Draw a conclusion from it.
- Say what the real constraint is.
What does a CDN do for a web application?
The strongest answer: It serves static files from servers near the user, so images, scripts and styles come from the edge with low latency and the origin only serves what changes
Why it works: Right. Static content at the edge, origin for the rest.
- Static files near the user.
- Lower latency for assets.
- Origin serves dynamic content.
- Cache headers control it.
The API has run on a single server for a year, and you are adding a second behind a load balancer.
What in the current design is likely to break?
The strongest answer: Anything kept on the box: sessions and caches in process memory, files written to local disk, in-process timers and locks — they move to a shared store, to object storage, and to one scheduler
Why it works: Right — the question is always what this instance knows that the other one does not, and the answer has to be nothing that matters.
- Ask what the box knows.
- State moves to a shared store.
- Uploads go to object storage.
- One scheduler, not one per instance.
Invoices and uploaded documents are streamed from object storage by the API, and that endpoint is now the slowest thing we run.
How do you put a CDN in front of private files without leaking them?
The strongest answer: Authorise in the API, then hand the browser a short-lived signed URL for that one object; the bucket stays private, the link expires in minutes, and the edge never caches a response keyed by user
Why it works: Right — the API keeps the decision, the edge moves the bytes, and the signature carries a permission the URL cannot outlive.
- Decide in the API; serve at the edge.
- Short-lived signed URL per object.
- The bucket stays private.
- Never cache per-user bytes by URL.
The recommendations API is 500ing. Checkout and search are fine.
What do you ship for a partial outage?
The strongest answer: Each widget fails on its own: read-only where writes are down, stale-labelled data where the secondary is down, and a page that still does the rest — not a single banner that blanks the product
Why it works: Exactly. Fullstack resilience is bulkheads in the UI and timeouts in the BFF. One dead recommendation service must not take down checkout.
- Bulkhead the UI by widget.
- Timeouts and defaults in the BFF.
- Label stale; do not lie.
- Do not retry-storm a limping API.
Someone enabled the CDN in front of the API and `/api/me` started looking very fast.
How do you cache a page that depends on the user?
The strongest answer: `Cache-Control: private` (or no-store) for personalised HTML/JSON, and if you cache at the edge, key on a signed cache identity — never cache `Authorization` or cookie-varying responses as `public` without Vary you actually honour
Why it works: Exactly. Caching personalised JSON at the edge is how Alice sees Bob's invoice. Private/no-store is the default; shared cache needs an explicit, tested key.
- Personalised → private or no-store.
- Shared cache only with a proven vary key.
- Never public-cache cookie responses by accident.
- `Vary: *` is not a strategy.