How to Assess System Design Skills in a Senior Software Engineer Interview
Run a useful senior system design interview with a realistic CSV import exercise, practical follow-up questions, example answers, and an evidence-based scorecard.
A candidate draws an API gateway, a queue, a cache, and six services. The boxes look familiar. The arrows make sense. Then you ask what happens if a worker crashes halfway through a job.
“The queue retries it.”
That might be the beginning of a good answer. It is not the end of one.
What has already been written? Can the same work run twice? What does the customer see while the job is recovering? Which failure would wake someone up at two in the morning?
Those questions reveal more about senior engineering judgement than the number of components on a whiteboard. A useful system design interview helps you see how someone makes decisions when requirements are incomplete and failures are ordinary.
Here is a practical way to run that interview. The example is a customer CSV import service for a SaaS product. It is small enough to discuss in an hour and rich enough to expose real design choices.
Decide what you are measuring
Before choosing a question, write down the work the person will actually own.
For a senior application engineer, I would usually assess six things: clarifying requirements, choosing boundaries, protecting data, handling failure, operating the system, and explaining trade-offs. A platform role may need more depth in infrastructure. A data role may need more depth in storage and processing. The interview should reflect that difference.
Do not quietly assess staff-level organisational strategy when the opening is for a senior engineer. Likewise, do not treat confidence with one cloud provider as proof that someone can design a reliable product workflow.
Define the expected evidence before meeting candidates. For example, “recognises that retries can repeat committed writes” is observable. “Has a senior mindset” is too vague to score consistently.
The US Office of Personnel Management's structured interview guidance describes using predetermined questions and common rating standards. You can apply that principle to a technical conversation without turning it into a rigid quiz. Keep the core problem, information, and main follow-ups consistent. Allow candidates to explain different valid designs.
Give the candidate a useful problem
Here is the prompt I would use:
We run a SaaS product where each company has its own customer records. An administrator needs to upload a CSV file containing up to 500,000 customers. Processing may take several minutes. The administrator should see progress and download a report of rejected rows. Retrying an upload must not create duplicate customers. Design the service and explain how it behaves when something fails.
Provide a short context sheet rather than making candidates guess the business:
| Detail | Starting assumption |
|---|---|
| Team | Four application engineers who also support production |
| Existing system | A web application, a relational database, and object storage |
| Import frequency | About 200 imports a day, with bursts during business hours |
| Customer identity | A stable external customer ID, unique within a company |
| Update rule | An import may update existing customer records |
| Error handling | Valid rows may succeed even when other rows are rejected |
| Product expectation | Imports usually finish within 15 minutes; this is an initial target to test |
These are fictional exercise assumptions, not production measurements. Share them when introducing the problem. Keep secondary details, such as retention and simultaneous imports, ready for clarification.
The stable customer ID matters. Without an agreed identity rule, “no duplicates” is an ambiguous requirement. Two rows with the same email address might represent one customer, two people sharing an address, or a correction. A candidate should not have to guess which interpretation your product uses.
Give candidates permission to use a familiar stack. You are evaluating the decisions behind a queue or transaction, not whether they remember a vendor's product names.
Make the hour predictable
Tell the candidate how the session will work. An interview is already an artificial setting; surprises add noise.
| Time | Focus |
|---|---|
| 0–5 minutes | Introductions, context, and the problem |
| 5–15 minutes | Requirements, assumptions, and rough sizing |
| 15–30 minutes | Main design and one complete data flow |
| 30–45 minutes | Two prepared failure scenarios |
| 45–55 minutes | Operations, trade-offs, and a final design review |
| 55–60 minutes | Candidate questions |
You will not investigate every subsystem. Choose the same two main failure scenarios for everyone. Keep a third for a follow-up session or additional time. Do not penalise a candidate for failing to cover topics you never gave them room to discuss.
If someone spends too long listing requirements, guide them: “We have enough to start. Please walk me through one upload.” If you provide that help, note it. Guidance is useful context for the score, not an automatic failure.
Listen to the first ten minutes
A strong beginning often sounds ordinary:
“Can a customer use the application before the import finishes?”
“Can two imports update the same customer at the same time?”
“Does cancelling an import undo successful rows, or stop the remaining work?”
These questions change the design. They uncover partial results, conflicting updates, and transaction boundaries.
Compare that with asking about the preferred message broker before understanding what must happen to the data. Tool questions can be useful later. At the beginning, they can hide the absence of a product model.
Rough sizing should also lead to a decision. Suppose every import were at the stated maximum. Two hundred imports would mean 100 million input rows a day, about 1,160 rows per second averaged across 24 hours. Most files may be much smaller, and the work will not arrive evenly.
The useful next question is about peaks and database work per row. A candidate who identifies expensive per-row lookups and proposes batch operations has connected the estimate to the design. Someone who announces a precise server count from the daily average has skipped several important assumptions.
You do not need mental arithmetic under pressure to get this signal. Let candidates use a calculator. You want to know what they choose to estimate and how they use the result.
Follow one import through the design
Ask the candidate to trace a file from the browser to the final report.
A reasonable starting design might work like this:
- An authenticated administrator creates an import job for their company.
- The service provides a restricted upload destination in object storage.
- After upload, the service verifies the object and makes the job available to workers.
- A worker reads the file in bounded batches, validates rows, and applies changes.
- The database records progress and the location of the error report.
- The browser polls a status endpoint until the job reaches a terminal state.
This is a reference path for discussion, not a hidden answer key. A database-backed job queue may be enough. A managed broker can also fit. Either choice needs a clear explanation of ownership and recovery.
Now slow down at the boundaries.
For the upload, how does the service prevent one company from processing another company's object? A strong answer keeps the company identity tied to the authenticated actor and the stored job. A company ID supplied by the browser is not enough.
For the worker, what fits inside one transaction? Holding a database transaction open for a 500,000-row file is a very different choice from committing batches. If batches commit independently, partial results become part of the product contract.
For progress, does “60% complete” mean bytes read, rows parsed, or rows committed? A candidate does not need a perfect percentage. They should recognise that misleading progress can become a support problem. Showing processed and rejected row counts may be more honest until the total is known.
For the completion report, what happens if the data commits but writing the report fails? The customer records and the report live in different systems. A good design either makes report generation recoverable from durable results or records enough state to resume it.
Notice what you are doing: examining one workflow deeply enough to find its promises. You do not need a diagram containing every possible infrastructure component.
Change one fact at a time
Use prepared changes to see whether the candidate can adapt their model.
A worker crashes after committing a batch
Ask: “The worker committed 1,000 rows, but crashed before acknowledging the job. What happens on the next attempt?”
Useful answers distinguish between retrying the import request and retrying individual work. An idempotency key on the HTTP request does not, by itself, protect the worker's database writes.
A candidate may propose a unique key on company and external customer ID, an upsert, and a checkpoint committed with each batch. Follow that answer one step further: does repeating the update overwrite a newer change made by a user or another import?
There are several defensible policies. The product might prevent overlapping imports per company, compare source versions, or explicitly accept last-write-wins updates. What matters is that the candidate notices the conflict and chooses a policy rather than assuming that an upsert makes every retry harmless.
One company submits 80 large files
Ask: “How do we keep other companies' small imports moving?”
Look for bounded concurrency, per-company limits, admission control, or scheduling that gives other companies a turn. Adding workers may help throughput but can also overload the same database.
Ask what the team would measure before changing capacity. Queue age, processing rate, database latency, and rejected work are more useful together than CPU alone.
The queue is unavailable after a job is saved
Keep this as the reserve scenario. The job exists in the database, but no worker receives it.
A candidate might suggest polling durable jobs or writing an outbox record in the same transaction as the job. The transactional outbox pattern addresses this database-and-message gap. The important follow-up is whether publishing can happen twice and how consumers handle that.
Naming the pattern is optional. Explaining the failure and a workable recovery path is the signal.
Score observable behaviour
Use a small scorecard while the conversation is still fresh. Here is an example for this role:
| Area | 1: Important gaps | 2: Developing | 3: Meets the senior bar | 4: Particularly strong |
|---|---|---|---|---|
| Requirements | Starts building without clarifying outcomes | Asks questions but misses key data rules | Clarifies identity, partial success, and constraints | Finds conflicts between requirements and helps resolve them |
| Boundaries | Cannot trace the workflow | Plausible components, unclear ownership | Clear API, storage, and worker responsibilities | Simplifies the design while preserving its guarantees |
| Data correctness | Assumes retries are harmless | Spots duplicates after substantial help | Explains atomic writes, uniqueness, and replay | Also handles conflicting updates and checkpoint recovery |
| Failure handling | Relies on restarting everything | Covers one failure in isolation | Handles retries, timeouts, and partial completion | Connects failure policy to user impact and recovery cost |
| Operations | Offers only generic logging | Names metrics without actions | Defines useful signals and support paths | Uses those signals to explain capacity and incident decisions |
| Trade-offs | Treats one stack as mandatory | Lists alternatives without choosing | Chooses a workable design for the stated team | Clearly explains what would justify changing it |
Record evidence beside each score. “Discussed retries” is weak evidence. “Explained the crash between batch commit and acknowledgement, then tied the checkpoint to the same transaction” is useful evidence.
Use “not observed” when time or interviewer choices prevented assessment. Do not convert missing evidence into the lowest score.
Calibrate the rubric before using it for real decisions. Have interviewers score the same practice answer independently, compare differences, and refine vague criteria. The numbers are a shared language for discussion. They are not a scientific measurement of someone's future performance.
Compare two answers to the same question
Consider the worker crash scenario.
Answer A: “I would use Kafka because it guarantees message delivery. If a worker dies, another worker takes over. We can scale consumers horizontally.”
There may be useful knowledge here, but the answer has not explained repeated database writes. Ask: “The database commit succeeded before the crash. Please walk through the replay.” Give the candidate a chance to supply the missing reasoning.
Answer B: “I expect the batch to run again. I would commit its changes and checkpoint together. The next worker checks that checkpoint before applying the batch. Customer identity needs a database constraint too. I would initially allow only one active import per company, because otherwise an older retry could overwrite a newer import.”
The second answer gives you more evidence. It identifies a failure window, proposes a boundary, and makes a product trade-off explicit. It may still need work: the interviewer can ask how the checkpoint identifies a batch and how user edits interact with imports.
A polished first answer can improve after a prompt. A detailed second answer can fall apart under another scenario. Score the full conversation, including the amount of guidance needed and whether the candidate revised an assumption thoughtfully.
Keep the interview fair and useful
Avoid turning clarification into a guessing game. If a candidate asks about retention, give the prepared answer. Do not reward the person who happens to discover the interviewer’s private requirement.
Let candidates sketch, write, or explain verbally. Give reasonable pauses for thinking. Clear communication means that you can follow their decisions; it does not require constant speech, perfect English, or beautiful diagrams.
Keep optional technology depth separate from the core score. A candidate who understands work leases and acknowledgement may use a broker you have never deployed. Ask about its behaviour before dismissing the choice.
Also watch your own tendency to favour agreement. If you prefer a managed queue and the candidate chooses a database queue, ask about contention, scheduling, and scale. They may have a sensible answer for four engineers and 200 imports a day.
For broader hiring signals, I use a separate discussion about senior engineering ownership. One design exercise cannot tell you everything about collaboration, delivery history, mentoring, or code quality.
Turn the notes into a hiring decision
Have interviewers write their scores and evidence before the debrief. This reduces the chance that the most confident person sets everyone else's interpretation.
A useful debrief sounds like this:
The candidate met our bar on requirements, data correctness, and trade-offs. They caught the replay problem without prompting and chose a reasonable first implementation. We did not get enough evidence on production diagnosis. A follow-up should ask them to investigate an import backlog using a small set of logs and metrics.
That is more actionable than “strong technically, maybe not senior enough.”
When evidence is mixed, name the unresolved risk and choose a focused way to examine it. Do not repeat the same broad interview and hope that another impression will settle the question.
The interview has done its job when you can explain which decisions the candidate made, why those decisions fit the role, and where your confidence is limited. A crowded whiteboard is easy to remember. The reasoning behind a safe retry is much more useful when you are deciding who will own the system.
