SevyDevy

Guide · Interview prep

System Design Interview Levels: Junior vs Senior vs Staff

25 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • System design interview levels are the bars a company sets for each job grade on the same design question. The gap is scope: how much of the problem you take ownership of, not how many boxes you draw.
  • A good junior answer is one simple design that works, explained clearly. It has a sensible API, meaning the requests the apps can make, and it takes hints well.
  • A good senior answer drives the interview. It agrees requirements, estimates load, names trade-offs (what each choice costs) and plans for failures before the panel asks.
  • A good staff answer also asks why the system exists and who else depends on it. It sets SLOs, measurable targets such as how fresh data must be, and plans rollout, cost and ownership.
  • Beginners can stop at the line that says so, after the drill for moving up a level. Engineers then get three tested checks: the load maths, a script that rejects late GPS pings in Redis (a fast in-memory store), and an average that hid 20 stuck orders.

What goes wrong when a correct design answer gets a lower level?

The design was correct, but system design interview levels are graded on how far an answer reaches, not only on whether it works. Panels note what you asked, what you weighed and what you protected against.

This often surprises people with years of experience, who then get a down-level: an offer one grade below the role they applied for. Kevin Landucci, a former recruiter who now coaches candidates on offers, writes that down-levels are almost never about coding rounds.

He says system design and behavioural interviews set your level instead. So the same question can earn three different levels, depending on what each answer shows.

Picture a common setup. A quick-commerce app delivers groceries in about ten minutes from small neighbourhood warehouses in one city, called dark stores. It is hiring a junior, a senior and a staff engineer in the same month. The setup is made up, but the gap between the answers is real.

Each candidate gets the same question: design live order tracking. Customers should see their rider move on a map until the bag arrives.

When asked, the panel shares a few numbers (made up). The app runs 40 dark stores and handles about 1.2 lakh (120,000) orders a day. At the evening peak, about 3,000 riders are on the road, each usually carrying two orders.

All three candidates pass at the level they applied for, yet their answers look very different. This guide walks through them side by side, so you can see what each level adds.

What are system design interview levels?

System design interview levels are the bars a company sets for each job grade, such as junior, senior and staff. The question can be the same, but a higher level must show wider scope, sharper trade-offs and more ownership.

Titles and years vary from company to company, so treat the grades as bands, not exact names. Many companies also have a mid-level band between junior and senior.

  • Junior engineer. Builds well-defined pieces with some guidance, and is expected to learn fast.
  • Senior engineer. Owns a whole feature or service end to end, makes sound choices alone and guides others on the team.
  • Staff engineer. Shapes technical direction across several teams, and takes on problems nobody has framed clearly yet.

Interview prep writers describe the same climb inside the round. Evan King of Hello Interview says mid-level candidates should guide the early parts, and the interviewer may take a more dominant role later.

He wants a senior candidate to go deep in around two places. A staff candidate is expected to lead almost the entire interview.

Staff roles also come in several shapes. Will Larson's StaffEng site describes four common archetypes. A tech lead guides one team, an architect owns a critical area, a solver digs into hard problems, and a right hand extends an executive's attention.

Think of a fishing boat crew off the coast of Goa. Each person on board looks after a bigger slice of the same trip.

  • The deckhand is the junior: hauls the net, sorts the catch well and asks when unsure.
  • The skipper is the senior: reads the weather, picks the fishing ground, decides how much ice to load and when to turn back.
  • The fleet owner is the staff engineer: thinks across many boats and seasons, and asks whether a shared ice plant would pay off.

Where the picture breaks: a deckhand rarely becomes a fleet owner by fishing better. In engineering, you climb by adding the next level's habits to work you already do well.

What does a live order tracking design need to do?

Live order tracking takes the rider's GPS position, links it to an order and shows it to the right customer within a few seconds. It also shows status changes, such as packed, picked up and arriving.

A few words come up in all three answers. Latency is how long something takes, here from the rider's phone to the customer's map. Polling means the customer's app asks the server for news again and again, on a timer.

Push means the server sends news as soon as it has it, over a connection that stays open. A WebSocket is one standard way to do that. RFC 6455, the WebSocket standard, gives a client and a server two-way messages over one TCP connection. TCP is the internet's standard way to carry data reliably between two machines.

A message queue is a buffer between programs: one side writes messages and another reads them later, at its own pace. Kafka is a popular example. It keeps messages after they are read, for as long as the topic's retention setting says, so other programs can read them again.

A cache is a fast copy of recent answers, usually kept in memory. Redis is a popular in-memory store often used this way.

How does a junior engineer answer a system design question?

A junior engineer answers with one simple design that works, explained clearly. The panel checks that the basics are right, that the candidate can reason, and that they take hints well.

The junior candidate starts drawing within a minute. The rider app sends its position to an order server every 4 seconds. The server saves each position in a table in MySQL, a popular open-source database. The customer app polls every 2 seconds for the latest one.

This is the naive version, and it is a fine place to start. In the diagram, every request touches the same server and the same table.

The junior answer

One server and one table do everything. Notice that every poll hits the database, even when the rider has not moved.

Diagram connections:

  • Rider app to Order server (Position every 4 seconds)
  • Order server to MySQL locations table (Insert and read rows)
  • Customer app to Order server (Poll every 2 seconds)
  • Rider app: Knows its GPS position
  • Order server: Saves positions, answers polls
  • MySQL locations table: One row per position ping
  • Customer app: Shows the rider on a map

The candidate does several things well. They write two clear endpoints, the addresses the apps call: one sends a position and one fetches the latest for an order. They add an index on order id, a lookup aid that finds rows without scanning the whole table.

Then the panel asks how many requests this creates. The candidate does the maths slowly but correctly: 3,000 riders pinging every 4 seconds make 750 writes a second.

With two orders per rider, up to 6,000 customers can have the tracking screen open. Polling every 2 seconds makes 3,000 reads a second, and about half of them find no new position.

The candidate sees the waste once it is pointed out. They suggest a cache in front of the database, which is a good instinct.

  • What made this a pass: a working end-to-end flow, a clear API, correct arithmetic, and taking hints without getting defensive.
  • What kept it at junior: no questions before drawing, no estimate until asked, and no thought about failures, such as a rider losing signal.

How does a senior engineer answer a system design question?

A senior engineer drives the interview from start to end. They agree the requirements first, estimate the load, compare options with reasons, and plan for failures before the panel asks.

The senior candidate does not draw for the first five minutes. They ask questions instead. Here is how the answer unfolds.

  1. Pin the requirements

    They ask how fresh the dot must be, and the panel says under 5 seconds is fine. They ask whether history must be kept, and learn support needs 30 days of it.

  2. Estimate the load

    They work out 750 position writes a second and up to 6,000 open screens. Polling every 2 seconds would mean 3,000 reads a second, about half of them wasted.

  3. Choose push over polling

    They pick WebSockets, so the server sends a position only when a new one exists, about 1,500 messages a second. Polling every 15 seconds stays as a fallback for bad networks.

  4. Split the write path

    Rider pings go through a small ingest service, the front door for pings, into a Kafka topic, a named stream of messages, keyed by order id. A tracking worker keeps only the latest position per order in Redis.

  5. Plan for failures

    Late pings are dropped by comparing the time each GPS fix was taken. If a rider loses signal for 20 seconds, the map shows a grey dot with the last update time.

  6. Name what to watch

    They track how old the newest position is for each active order, at the 99th percentile: the age 99% of orders stay at or under. That number shows when tracking is quietly getting slow.

The design has more parts than the junior one, but each part has a reason the candidate can state. Follow the rider's ping from the left.

The senior answer

Writes and reads are split. Notice that only the latest position per order lives in memory, and history is written off the live path.

Diagram connections:

  • Rider app to Ingest service
  • Ingest service to Kafka location topic
  • Kafka location topic to Tracking worker
  • Kafka location topic to 30-day ping history (Archived in batches)
  • Tracking worker to Redis latest position (Write newest position)
  • Tracking worker to WebSocket gateway (Push the change)
  • WebSocket gateway to Redis latest position (Last spot on reconnect)
  • WebSocket gateway to Customer app
  • Rider app: Sends a ping every 4 seconds
  • Ingest service: Checks the rider and the order
  • Kafka location topic: Buffers pings, keyed by order id
  • Tracking worker: Drops late pings by fix time
  • Redis latest position: One small record per active order
  • WebSocket gateway: Holds open customer connections
  • Customer app: Moves the dot on each new message
  • 30-day ping history: For support and later analysis

Why the 99th percentile? A percentile is the value a given share of measurements stays at or under, so 99% of orders sit at or below the 99th. Google's Site Reliability Engineering (SRE) book warns that a simple average can hide slow outliers.

The same chapter says a high percentile such as the 99th shows a plausible worst case. Once 1% of orders are stuck, the 99th percentile shows them, even while the average still looks healthy. You will run this check yourself later.

The panel pushes back on one choice. Why Kafka, when the ingest service could write to Redis directly? The candidate answers that Kafka keeps pings after they are read, so the history job and later readers share one stream without slowing the live path.

They also admit Kafka is one more system to run, and say direct writes would be fine at a tenth of the load. That last sentence is the senior signal: what the choice costs, and when they would choose differently.

How does a staff engineer answer a system design question?

A staff engineer first asks why the system matters to the business and who else will use it. Then they design for those needs, set measurable goals, and plan how the system fails safely, ships and grows.

The staff candidate opens with a question the other two never asked: why do customers open the tracking screen at all? The panel shares support data (made up). Most calls are customers asking where their order is.

So the real goal is fewer anxious calls, not a pretty moving dot. The candidate argues that a reliable status line and an honest arrival time calm customers more than the dot does. The dot is still built, but it is no longer the part that must never fail.

One location stream, many users

Rider positions are useful far beyond the customer screen. Support agents need them during calls. The arrival time estimate, or ETA, needs them. Store managers need them to see which riders are about to come back.

So the candidate proposes one shared location stream that several teams read, owned by one platform team. This stops three teams from building three copies of the same pipeline.

The staff answer

One owned stream feeds several teams. Notice the question box: when the position is stale, the screen falls back to status steps.

Diagram connections:

  • Rider app pings to Shared location stream
  • Shared location stream to Customer tracking
  • Shared location stream to ETA service
  • Shared location stream to Support console
  • Customer tracking to Position under 10s old?
  • Position under 10s old? to Moving rider dot (Yes)
  • Position under 10s old? to Status steps and ETA (No)
  • ETA service to Status steps and ETA (Feeds the arrival time)
  • Customer tracking to Freshness goal dashboard (Freshness numbers)
  • Rider app pings: Position and battery level
  • Shared location stream: Owned by one platform team
  • Customer tracking: Pushes updates to open screens
  • ETA service: Re-estimates arrival time
  • Support console: Agents see the rider during calls
  • Position under 10s old?: Checked for every open screen
  • Moving rider dot: The full live view
  • Status steps and ETA: Packed, picked up, arriving soon
  • Freshness goal dashboard: Alerts when the goal is at risk

Goals you can measure

The candidate writes the goals as SLOs. The SRE book defines an SLI, or service level indicator, as a carefully defined measure of the service. An SLO, or service level objective, is a target value for that measure.

There are two goals. Status changes reach 99.9% of customers within 3 seconds. Rider positions are under 10 seconds old for 99% of active orders. The stricter goal sits on status, because status is what stops the support calls.

Cost, rollout and people

The candidate also asks about cost. Riders waiting at a dark store are not moving, so their phones can ping every 8 seconds instead of 4. If 1,000 riders are waiting, their pings drop from 250 to 125 a second, which also saves battery.

  • Rollout. Launch in two dark stores first, behind a switch that can turn the live dot off without an app release.
  • Privacy. Show a rider's position only between pickup and delivery, never after the shift ends, and delete raw history after 30 days.
  • Ownership. One team owns the stream and its freshness goal. Other teams read it, but do not change its format without notice.
  • Later. If the business wants demand heat maps, H3, an open-source hexagon grid first built for Uber's data science needs, could group positions into areas.

Notice what the candidate skips. They spend little time on WebSocket gateway details, because senior-level depth there is assumed. When the panel asks, they go deep in one area and then return to the big picture.

What is the difference between senior and staff system design answers?

A senior answer solves the stated problem very well, while a staff answer checks whether it is the right problem and designs for everyone it touches. Senior owns a system; staff owns its place among other systems.

Back on the boats, the senior candidate is the skipper who brings one boat home full and safe in rough water. The staff candidate is the fleet owner asking whether twelve boats should share one ice plant, and who pays.

The table puts all three answers side by side. Read across a row to see how one skill grows with level.

What each candidate showed on the same question (figures made up)
SignalJunior answerSenior answerStaff answer
RequirementsStarted drawing at onceAsked freshness, history and scale firstAsked why customers open the screen
EstimationCorrect maths when asked750 writes and 6,000 screens, unpromptedCut idle pings to save cost and battery
Core designPoll one server and one tablePush, queue, in-memory latest positionShared stream for several teams
Trade-offsAdded a cache after a hintSaid when Kafka is not worth itPut the stricter goal on status, not the dot
FailuresNot coveredLate pings, lost signal, reconnectsFallback screen when data is stale
Beyond the systemNot expectedWatched one key numberGoals, rollout, privacy and ownership
Who led the talkThe panelThe candidateThe candidate, with the panel as partners

What mistakes push a candidate down a level?

The most common mistake is answering below the role, for example a senior who waits for the panel to ask about failures. The second is the reverse: reaching for a staff-sized design without the basics.

  • Drawing before asking. You may solve the wrong problem, and the panel cannot see your judgement.
  • Naming tools without reasons. Saying Kafka or Redis is not a design. Say what each one does for this problem.
  • Treating every choice as obvious. If you never say what a choice costs, the panel cannot tell whether you know.
  • Going too big too early. Several regions (copies of the system in separate data-centre locations) for a single-city app at 750 writes a second shows a poor sense of scale.
  • Never going deep. Staying at the box level for the whole round hides whether you can build any part of it.
  • Ignoring the clock. Spending most of the round on requirements leaves no time for the design itself.

The junior candidate could draw before asking and still pass, because junior bars are lower. A senior candidate could not. The same habit costs more as the level rises.

How do you raise your system design answer by one level?

You raise your answer by one level by adding the habits of the next band to the answer you already give. Practise one habit at a time on the same question until it feels natural.

  1. Junior to mid: ask before you draw

    Spend the first few minutes on who uses the system, what it must do and how big it is. Write the answers where the panel can see them.

  2. Mid to senior: estimate unprompted

    Turn users into requests per second and storage per day. Use the numbers to justify each choice, like the senior candidate's point about wasted polls.

  3. Senior: compare two options

    For each big choice, name a second option. Say why you picked one, what it costs, and when you would switch.

  4. Senior: break your own design

    Before the panel asks, walk through a part failing. Ask what the user sees, and how you would notice.

  5. Senior to staff: zoom out

    Ask why the system exists, who else depends on it, and how it ships safely. Set measurable goals and name the owner.

  6. Staff: zoom back in

    Pick the riskiest part and go deep on it. Show you can still build what you propose.

A good drill is to answer one question three times: first as the junior candidate did, then as the senior, then as the staff. Record yourself, and listen for where your answer stops growing.

How do you check the load numbers in a senior answer?

Write the estimate as a few lines of code and run it, so every number traces back to an assumption. Here is the senior candidate's maths for the evening peak.

python
riders = 3000          # riders on the road at the evening peak (made up)
orders_per_rider = 2   # orders each rider carries on one trip (made up)
ping_every_s = 4       # the rider app sends one GPS ping every 4 seconds
poll_every_s = 2       # a polling customer app asks again every 2 seconds

screens = riders * orders_per_rider
writes = riders / ping_every_s
polls = screens / poll_every_s
pushes = writes * orders_per_rider

print(f"open tracking screens: {screens:,}")
print(f"position writes: {writes:,.0f} per second")
print(f"polling reads: {polls:,.0f} per second")
print(f"push messages: {pushes:,.0f} per second")
print(f"polls that find no new position: about {1 - poll_every_s / ping_every_s:.0%}")

Save it as load_maths.py and run python3 load_maths.py (tested on Python 3.13.16). You should see these five lines.

text
open tracking screens: 6,000
position writes: 750 per second
polling reads: 3,000 per second
push messages: 1,500 per second
polls that find no new position: about 50%

What just happened? Push halves the position messages here, from 3,000 polls to 1,500 pushes a second, and every push carries a new position. Polling also adds up to 2 seconds of delay, because the app only learns of a ping at its next poll.

Now change one input, say orders per rider to 1, and run it again. Saying which input drives which number is what turns a guess into a senior estimate.

How do you stop a late GPS ping from moving the dot backwards?

Store each position with the time of its GPS fix, the moment the phone read its position. Save a new ping only if it is newer than the stored one. Do the check and the write as one atomic step, meaning nothing else can run between them.

Kafka alone does not solve this. Its docs say events with the same key go to the same partition, one ordered part of a topic, and are read in the order written. That holds while the partition count stays the same; the operations docs warn that adding partitions can shuffle keys.

Written order is arrival order, not the order the phone took its fixes. If the phone resends an older ping after a signal drop, the ingest service writes it after the newer one, and Kafka delivers it in that order.

The fix below runs a small Lua script inside Redis: a few lines in a scripting language the Redis server runs itself. The Redis docs say a script runs atomically and blocks all other server activity while it runs, so keep scripts this short. The key name goes in through KEYS, as the docs ask, so the script also suits a Redis cluster.

python
import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

KEEP_NEWEST = """
local stored = redis.call('HGET', KEYS[1], 'ts')
if stored and tonumber(stored) >= tonumber(ARGV[1]) then
  return 0
end
redis.call('HSET', KEYS[1], 'ts', ARGV[1], 'lat', ARGV[2], 'lng', ARGV[3])
return 1
"""
save_if_newer = r.register_script(KEEP_NEWEST)

key = "latest:order:42"
r.delete(key)

# The phone took ping A first and ping B four seconds later (Unix seconds).
# Ping B reaches the worker first. Ping A arrives late, after a signal drop.
print("ping B saved:", save_if_newer(keys=[key], args=[1760000008, 28.4601, 77.0263]))
print("ping A saved:", save_if_newer(keys=[key], args=[1760000004, 28.4598, 77.0251]))
print("dot shows:", r.hgetall(key))

Start Redis locally on its default port, install the client with pip install redis==8.1.0, and run the file. On Redis 7.0.15 with redis-py 8.1.0, it prints these three lines.

text
ping B saved: 1
ping A saved: 0
dot shows: {'ts': '1760000008', 'lat': '28.4601', 'lng': '77.0263'}

What just happened? Ping A was rejected because its fix time is older, so the dot stays on ping B. Each order's position is a Redis hash, one small record of named fields.

In our test, writing the same two pings with a plain HSET, the command that sets fields in a hash, left the older ping A stored. The script also ignores an exact duplicate ping, because the check uses greater-than-or-equal.

Why measure freshness at the 99th percentile, not the average?

Because stuck orders barely move the average, while the 99th percentile jumps once they reach about 1% of orders. In this test, 20 of 1,000 active orders have riders whose pings stopped 90 seconds ago.

python
import statistics

# Age of the newest position, in seconds, for 1,000 active orders (made up).
# 980 orders are healthy; 20 are stuck because their riders' pings stopped.
ages = [2.0] * 980 + [90.0] * 20

mean = statistics.fmean(ages)
p99 = statistics.quantiles(ages, n=100)[98]
fresh = sum(age < 10 for age in ages) / len(ages)

print(f"average age: {mean:.2f} s")
print(f"99th percentile age: {p99:.2f} s")
print(f"orders under 10 s old: {fresh:.1%}")

Run it with python3 (tested on Python 3.13.16). The quantiles function with n=100 returns 99 cut points, using its default exclusive method, and index 98 is the 99th percentile.

text
average age: 3.76 s
99th percentile age: 90.00 s
orders under 10 s old: 98.0%

What just happened? The average of 3.76 seconds looks healthy against the 5-second freshness target. Yet only 98% of orders are under 10 seconds old, so the staff candidate's 99% goal is already broken.

The 99th percentile of 90 seconds shows the problem at once. Those 20 customers are the ones most likely to phone support. This is why the senior candidate watches a percentile, and the staff candidate writes the goal as a share of orders.

The 99th percentile has a blind spot of its own. We reran the test with fewer stuck orders: with 9 of 1,000 stuck, it stayed at 2 seconds, and it jumped only at 10. That threshold depends on the method: with Python's inclusive method, 10 stuck orders still gave just 2.88 seconds.

For smaller groups, count stuck orders directly. Over 1,000 orders, the 99.9th percentile is close to the single worst order, so it fired on even one stuck order in our test.

How do you show your level in a system design interview?

You show your level through the questions you ask, the trade-offs you name and how much of the conversation you lead. Tell the panel what you are doing as you do it, so they can give you credit.

  1. Say your plan

    Open with the order you will follow: requirements, numbers, design, then failures. The panel can then steer you, instead of guessing.

  2. Ask questions that change the design

    Ask about freshness, scale, history and purpose. Skip questions whose answer would not change anything you draw.

  3. Give the simple version first

    Draw a design like the junior answer in two minutes. Then improve it step by step, with a reason for each change.

  4. Go deep where it is risky

    Choose the hardest part, such as late pings or reconnects, and walk through it in detail.

  5. Close with what you would watch

    End with one or two measurable goals, the main risks and what you would build next.

Questions people ask

Do junior engineers get system design interviews?
Sometimes, usually in a lighter form. The Tech Interview Handbook says system design questions typically go to candidates with a few years of experience, and interns are rarely given them. When juniors do get one, the panel checks for a working design, a clear explanation and taking hints. Nobody expects a fresher to plan rollouts or team ownership.
How do interviewers decide my level in a system design round?
They compare what you showed with the bar for each level. They look at how you gathered requirements, estimated load, weighed options, handled failures and led the talk. A level needs steady evidence across the round, not one good moment, and the exact bar differs between companies.
Can I get down-levelled after a system design interview?
It can happen. A down-level is an offer one grade below the role you applied for. Kevin Landucci, a former recruiter turned offer coach, writes that down-levels are almost never about coding rounds. A solid answer that skips trade-offs and failures can read as one band lower.
What do staff engineers talk about in system design interviews?
They talk about why the system exists and who depends on it, as well as how it works. Expect measurable goals, cost, safe rollout, data privacy and team ownership. A strong staff candidate still goes deep on one hard technical part, so the big picture stays grounded.
How many years of experience do you need for a senior system design interview?
There is no fixed number. Companies level by what you can show, not by years alone. If you can lead a design, estimate load and defend trade-offs on your own, you can aim for a senior loop. Ask your recruiter how their levels are calibrated.
Is drawing more components a sign of a senior answer?
No. More boxes without reasons often look worse, not better. The senior design in this guide has eight parts, but the candidate can give each a reason and say when to remove it. A simple design with clear trade-offs beats a crowded diagram you cannot explain.
Should I use WebSockets or polling for live tracking in an interview?
Either can be right, so explain the choice. Polling is simple but sends many requests that find nothing new. WebSockets push only real changes over an open connection, but each open connection holds some server memory. Many designs use push with polling as a fallback.

Sources and further reading

Practise this with Lala

Start