What the senior bar actually tests
At senior level the system design round is not checking whether you can name Kafka or Redis. It checks whether you can take a vague prompt, turn it into a scoped problem, propose an architecture that survives follow-up questions, and explain the cost of every choice. Junior candidates recall a reference architecture. Senior candidates reason from requirements.
The interviewer is looking for a few specific signals. Can you find the real constraint hiding in a loose prompt. Do you separate what must be consistent from what can lag. Do you know where your design breaks and say so before being pushed. Do you connect a technical choice to an operational consequence, like on-call load or cost per request.
If you only practise drawing boxes, you will pass the first five minutes and stall on the deep dive, where offers are won or lost.
It helps to know how the interviewer is scoring you. Most large companies use a rubric, and the categories are consistent across firms even when labels differ.
| Signal | Weak answer | Strong answer |
|---|---|---|
| Requirements | Starts drawing immediately | Extracts numbers and an explicit scope before any boxes |
| Data modelling | Picks a database by reputation | Lists access patterns, then maps each to a store |
| Tradeoffs | Defends one design to the death | Names the tension, picks a side, states what would change the call |
| Scale | Hand-waves "it will scale" | Estimates load, finds the first bottleneck, sizes the next step |
| Operations | Stops at the happy path | Covers failure, retries, observability, and on-call cost |
| Communication | Goes silent while thinking | Narrates reasoning so the interviewer can steer |
The left column is not false, it is weak because it skips the reasoning the interviewer is buying. The same architecture, delivered with the reasoning visible, scores a full band higher.
A structure you can repeat under pressure
Use the same opening every time so you do not freeze. The structure below takes about forty minutes and leaves room to redirect.
- Clarify the product and the users. Ask who uses this, how often, and what they expect.
- Pin down scale. Get rough numbers for daily active users, read and write ratio, and payload size.
- State explicit non-goals for the session.
- Define the core entities and the main API calls.
- Sketch the high level data flow.
- Choose storage by access pattern, not by brand.
- Add caching, queues, and the handling of uneven load.
- Cover reliability, abuse, and observability.
- Name what you would improve with more time.
Write these steps on the whiteboard or shared doc. It tells the interviewer you have a method, and gives you a checklist when you blank.
A rough time budget keeps you honest. In a forty-five minute slot, aim for five to eight minutes on requirements and scale, five on the high level design, fifteen to twenty on the deep dive, and the last five on failure modes and improvements. Candidates who spend twenty-five minutes on a beautiful box diagram and never reach the deep dive underperform, because the deep dive is where the rubric has the most points.
A senior round is a conversation, not a presentation. The structure exists so you can recover when the interviewer interrupts, not so you can deliver a script.
Turn a vague prompt into numbers
If the prompt is "design a notification system," do not start designing. Ask questions that produce numbers, not features.
- How many notifications per day at peak.
- Email, push, SMS, or all three.
- Is a one minute delay acceptable, or must some be near instant.
- Can a notification be dropped, or must every one be delivered at least once.
Suppose you land on fifty million notifications per day, mixed channels, most tolerating a minute of delay, none silently dropped. That paragraph already implies a queue, a per-channel sender with retries, idempotency keys, and a dead letter path.
Now convert the headline number into a rate. Fifty million per day is roughly 580 per second on average, and traffic is never flat, so a peak of three to five times that puts you near 2,000 to 3,000 sends per second at the busy hour. That peak figure, not the daily total, is what you size queues and worker pools against. Doing this arithmetic out loud shows you design for peak, not the comfortable average.
Make storage choices defensible
The weakest senior answers say "use a NoSQL database" with no reasoning. The strongest describe the access pattern first, then pick a store that serves it.
For a notification system the access patterns might be:
- Append a notification record for a user, very high write volume.
- Read recent notifications for one user, ordered by time.
- Mark a notification as read, single row update.
- Fan out one event to many recipients.
That pattern fits a wide-column or key-value store partitioned by user id, with time as the sort key. A relational database can work at smaller scale; naming the point at which you would move off it is a senior signal, because it shows you know the choice has a lifespan.
type Notification = {
userId: string;
notificationId: string;
channel: "push" | "email" | "sms";
createdAtMs: number;
readAtMs?: number;
};Keep records compact. Store a reference and hydrate the full payload in batches rather than copying large bodies into every row.
The deep dive on storage almost always lands on the partition key, so have an opinion ready. Partitioning by user id keeps one user's notifications together, serving the "read my recent notifications" pattern cheaply. The risk is a hot partition: a single user or broadcast can concentrate writes. Be ready to say how you would spread that load, for example a bucket suffix on the key for very hot users, or routing broadcast fanout through a separate path. The interviewer is testing whether you reason about skewed data.
This is not a hypothetical concern. Amazon's own account of running DynamoDB describes exactly this failure and the mitigation: when a partition takes sustained high throughput the service splits it into two, each holding a subset of the items, a technique its engineers call split for heat. Naming that a managed store already does this, and that your key choice decides whether it can, is the kind of concrete reference that reads as experience rather than theory.
A short comparison helps you justify the call rather than assert it:
| Store type | Good fit when | Cost you accept |
|---|---|---|
| Relational | Strong consistency, joins, moderate scale | Harder to shard, write ceiling per node |
| Wide-column or key-value | High write volume, known access patterns | Denormalised data, limited ad hoc queries |
| Document | Flexible schema, read-mostly aggregates | Weaker cross-document consistency |
| Search index | Full-text or faceted lookups | Eventual, synced from a primary store |
Treat tradeoffs as the main event
Every interesting design has a tension: strong reads versus cheap writes, freshness versus cost, simplicity versus scale. Your job is to surface it and pick a side with a reason.
Take fanout for an activity feed. Fanout on write makes reads cheap but punishes accounts with millions of followers. Fanout on read keeps writes cheap but makes every feed load expensive. The senior answer is usually a hybrid: precompute for normal accounts, compute at read time for the few very large accounts, and merge the two. State the threshold as tunable config, not a constant, because real systems tune it with live metrics.
function chooseFanout(followerCount: number, threshold: number) {
return followerCount >= threshold ? "fanout_on_read" : "fanout_on_write";
}
console.log(chooseFanout(2_000_000, 100_000)); // "fanout_on_read" (a celebrity, computed at read time)
console.log(chooseFanout(180, 100_000)); // "fanout_on_write" (a normal account, precomputed)When the interviewer pushes back, do not defend the answer to the death. Say what new information would change your mind. That flexibility reads as seniority.
Name the consistency model explicitly, because it is the tradeoff interviewers probe hardest. For a feed or notification list, eventual consistency is almost always fine: a follower seeing a post half a second late costs nothing. For a "mark as read" count or a billing event, you want stronger guarantees. A line like "the unread badge can be eventually consistent, but the payment confirmation cannot" shows you treat consistency as a per-operation decision, not a global switch.
Show that you have run things in production
Senior interviewers care about what happens after launch. Raise the parts that show up only on call.
- Rate limit writes so one misbehaving client cannot flood the queue.
- Make retries idempotent with a natural key, so a replayed job does not double send.
- Put fanout on a queue so a post does not block on millions of writes.
- Track lag from event to delivery, queue depth, and failure rate per channel.
Mentioning a dead letter queue, a poison message policy, or a backpressure plan separates people who have operated systems from those who have only read about them. When the interviewer pushes on what happens at the busy hour, reach for the reliability move that Google's site reliability engineers document: rather than let the whole system tip over, shed load, serving degraded responses that are cheaper to compute, or dropping a fraction of traffic upstream once total load exceeds capacity. Saying "I would return a lighter response before I would fall over" is the difference between a design that survives a spike and one that cascades.
Take idempotency further, because it is where many candidates wave their hands. It means the sender can process the same job twice and produce one outcome, usually via a key derived from stable inputs, recorded on first success, and checked before any side effect runs. This is not an interview abstraction: Amazon builds its own public APIs this way, letting a caller pass a client request token so that, in their words, requests with the same identifier can be treated as duplicate requests and dealt with as one.
The core is small enough to run. A store that only claims a key if it is unseen, and a channel that records each delivery, shows the guarantee directly: two calls with the same key produce one delivery.
const seen = new Map<string, string>();
const delivered: string[] = [];
const store = {
async putIfAbsent(key: string, value: string) {
if (seen.has(key)) return false;
seen.set(key, value);
return true;
},
async put(key: string, value: string) { seen.set(key, value); },
};
const channel = {
async deliver(job: { userId: string }) { delivered.push(job.userId); },
};
async function sendOnce(job: { idempotencyKey: string; userId: string }) {
const claimed = await store.putIfAbsent(job.idempotencyKey, "in_progress");
if (!claimed) return; // already handled, or a retry of an in-flight job
await channel.deliver(job);
await store.put(job.idempotencyKey, "done");
}
await sendOnce({ idempotencyKey: "n-42", userId: "u1" });
await sendOnce({ idempotencyKey: "n-42", userId: "u1" }); // a retry of the same job
console.log(delivered); // ["u1"] -> one delivery despite two callsThat code is deliberately small, but it lets you talk about the real problems: the time-to-live on the key, what happens if the worker dies after delivery but before marking done, and whether the downstream channel is itself idempotent. Walking through one of those failure windows out loud pushes an answer from "competent" to "senior". You need only show you know the window exists.
Aim the deep dive at the team you are interviewing with
The high level design is nearly the same whoever asks. The deep dive is where you should read the room, because the same prompt is weighted differently by the team that owns the round. The signal a senior candidate sends is that you know which fifteen minutes matter to the person across the table, and you steer there without being told.
- A backend or platform team pushes on the data layer: partition keys, queue semantics, exactly-once versus at-least-once, and what happens when a downstream store is slow. If you sense this, spend your deep dive on the storage and consistency argument, not the API surface.
- An infrastructure or site reliability team dwells on capacity planning, deployment, and detection. Lead with the failure story: how you notice the outage, how you shed load, how you roll back. A clean incident narrative earns more here than a clever data model.
- A product-leaning team cares more about how the design serves changing requirements than the last ten percent of throughput. Show where the schema and the contracts leave room for the feature they have not asked for yet.
There is a second axis under the first: how much you are expected to drive. A senior candidate is judged on naming and defending tradeoffs and raising operational concerns unprompted. A staff-level bar goes further, questioning whether the requirement itself is right and reasoning about migration and organisational cost, not just the diagram. You do not have to guess your band. Ask early who owns this system in production, then aim the deep dive at that team. To rehearse against the real weightings, work through the backend engineer interview questions or the platform engineer interview questions before the loop.
The opening minutes of a URL shortener, taken apart
Here is how the opening minutes can sound when it goes well. The prompt is "design a URL shortener".
Interviewer: Design a service that turns long URLs into short ones.
Candidate: Before I design anything, how many new short links per day, and how many redirects per link.
Interviewer: Say ten million new links a day, and reads outnumber writes about a hundred to one.
Candidate: So this is read-heavy, around a billion redirects a day. The redirect path has to be fast and cacheable, and the write path can be simpler. Two non-goals for now: I will skip analytics dashboards and vanity domains. Is that fair.
Interviewer: That is fine. Where do you start.
Candidate: The core entity is a mapping from a short code to a long URL, and the hot operation is "given a code, return the URL", so I optimise for that. I will generate codes from a counter encoded in base62 rather than hashing, to avoid collision checks, and cache the hottest codes in front of the store, since a small fraction of links take most of the traffic.
In four turns the candidate extracted numbers, named the read-heavy shape, set non-goals, found the hot path, and justified two choices, all before drawing a single box.
Where senior rounds are actually lost
A few mistakes show up again and again.
- Drawing microservices before establishing requirements. Boxes without numbers look like memorisation.
- Treating a tool name as an explanation. "Use Kafka" is a noun, not a design.
- Ignoring the skewed cases, like the celebrity account or the one tenant a hundred times larger than the rest.
- Going silent during the deep dive. Think out loud so the interviewer can follow and help.
- Refusing to admit a weakness. Every design has one; naming it first builds trust.
- Over-engineering for a scale nobody asked for. Five services for a thousand users signals poor judgement.
- Losing the thread on time. A gorgeous diagram with no deep dive scores below a rough one with a thorough deep dive.
How to practise before the loop
Pick five canonical problems and run the full structure on each, out loud, on a timer. A rate limiter, a URL shortener, a chat system, a news feed, and a metrics pipeline cover most patterns you will face. Record yourself, then check whether you scoped, sized, chose storage by access pattern, and named tradeoffs. The goal is not a perfect diagram. It is a calm, repeatable method that holds up when the interviewer changes the requirements halfway through.
Practise the back-of-the-envelope arithmetic separately until it is automatic: the rough order of magnitude for a read from memory, an SSD, and a round trip across a data centre, and turning a daily figure into per-second and peak rates without a calculator. Colin Scott's interactive version of Jeff Dean's latency numbers every programmer should know is worth internalising until the gaps between memory, disk, and network are muscle memory, because that is what lets you spot the real bottleneck out loud. When that is fluent, you free up attention for the tradeoffs and failure modes that win the round. A mock interview with another engineer beats five solo runs, because the value is in handling interruptions you cannot predict.
Frequently asked questions
How much should I drive versus follow the interviewer. Drive the opening, then follow. Own the requirements and the high level shape, where you show method, then go where the chosen deep dive points. Fighting their direction to finish your own plan reads as inflexibility.
What if I do not know a technology they mention. Say so plainly, then reason about the properties you would need. "I have not used that queue, but I need at-least-once delivery and ordering per key." Honest reasoning beats a bluff that collapses on the first probe.
Do I need to memorise reference architectures. No. Patterns are worth knowing, but memorised diagrams tempt you to skip the requirements. Understand why each pattern exists and when it stops fitting, and you can rebuild any of them from first principles.
How technical should the code be. Optional and small. A snippet that pins down an interface or a tricky bit of logic adds clarity. A full implementation wastes time the deep dive needs.
Sources
- Malcolm Featonby, Making retries safe with idempotent APIs, Amazon Builders' Library.
- Handling Overload, chapter 21 of the Google Site Reliability Engineering book.
- Scaling DynamoDB: how partitions, hot keys, and split for heat impact performance, AWS Database Blog.
- Colin Scott, Latency Numbers Every Programmer Should Know, an interactive update of Jeff Dean's figures.
Where to take this design practice next
Run the full structure on adjacent canonical problems, then rehearse against the question sets for the team you are interviewing with:
- Design a rate limiter, a compact prompt to drill scoping and tradeoffs on a timer.
- Backend system design deep dive, for the storage and consistency reasoning that carries the deep dive.
- Design a Twitter clone, the canonical fanout and feed problem worked end to end.
- Backend engineer interview questions to pressure-test the data-layer follow-ups.