The same question, answered by a junior and a senior: eight examples

Seniority in an interview is not measured by how much you say. Every answer below is correct. Only one of each pair gets you the offer, and the difference is smaller and more learnable than most people expect.

PracticeDepth team8 min read

When engineers ask what a senior answer sounds like, they usually get told to be more confident, or to talk about impact. That advice is not wrong but it is unusably vague.

Here is something more concrete. In pair after pair below, the senior answer differs in the same four ways: it names the mechanism underneath, it points at a specific situation rather than the general case, it volunteers the cost, and it says what it would measure. Nothing else. Once you can see it, you can do it.

1. JavaScript closures

What is a closure?

Junior answer

A closure is a function that remembers the variables from the scope where it was defined, so it can still use them later even after that function has returned.

Senior answer

It is a function together with a reference to the scope it was created in, so the variables it captured stay alive on the heap instead of dying with the call. That is what makes module patterns and hooks work, and it is also the classic memory leak: hold a closure over something large in a long-lived handler and it is never collected. It also explains the loop bug people hit with var, since one shared binding gets captured instead of one per iteration.

The follow-up here is almost always the loop bug or the leak. If you volunteered both, you have already answered it.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3, 3, 3 -- one binding of i, shared by all three closures

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 0, 1, 2 -- let creates a fresh binding per iteration
The version of this that gets shown in interviews. Knowing that it prints 3, 3, 3 is table stakes; being able to say why in terms of bindings is the answer.

2. React re-renders

How would you fix a slow React page?

Junior answer

I would wrap the expensive components in memo and use useMemo and useCallback so they do not re-render unnecessarily.

Senior answer

First I would find out whether it is actually a render problem. The profiler tells you whether you are re-rendering too often, rendering too much at once, or blocking on something that is not React at all. On the last page I did this for, it was a context holding a new object every render, so every consumer re-rendered on every keystroke. Splitting the context fixed it. I reach for memo after measuring, because sprinkling it everywhere adds comparison cost and a lot of stale-closure bugs for people who do not read the dependency arrays.

The junior answer invites 'and what does memo compare?'. The senior one has already moved the conversation to diagnosis, which is where they wanted to be.

3. The Node event loop

Why is Node considered good for I/O heavy work?

Junior answer

Because it is non-blocking and asynchronous, so it can handle many requests at once without waiting for each one to finish.

Senior answer

Because the I/O is handled off the main thread and the callbacks come back to a single loop, so thousands of connections cost you very little while they are just waiting. The flip side is that anything CPU bound on that thread stops everything. I have watched a synchronous JSON parse of a large payload add hundreds of milliseconds to every concurrent request on the process, which does not show up in local testing at all. That work belongs in a worker or a different service.

The senior answer contains its own failure mode, which is exactly what the next question was going to be.

4. Database indexes

When would you add an index?

Junior answer

When a query is slow, or on columns you filter and join on, to avoid scanning the whole table.

Senior answer

When the plan shows a scan or a sort that the query does not need, and the table is big enough for it to matter. The order of the columns is the part people miss: an index on a filter column plus a sort column only helps if the leading column is the one you filter on. And the cost is real, every write maintains every index, so on a write-heavy table I have removed indexes to speed things up. I would look at the plan before and after rather than guessing.

Composite column order is the standard follow-up, and it filters out a lot of people who have only read about indexes.

5. REST and retries

What does idempotent mean and why does it matter?

Junior answer

It means making the same request more than once has the same effect as making it once. GET, PUT and DELETE are idempotent, POST is not.

Senior answer

It means the caller can safely retry, which matters because at some point they will. A network timeout does not tell you whether the write happened, so any client with retries will eventually double-submit. For payments we took an idempotency key from the client, stored it with the result on first use, and returned that stored result on a repeat. The interesting part is the race between two concurrent retries, which needs a unique constraint rather than a check-then-insert, because check-then-insert loses that race under load.

Naming the concurrent-retry race is the single strongest signal in this answer. It is what someone who has actually shipped this says.

6. Caching

How do you decide what to cache?

Junior answer

Data that is read often and does not change much, with a TTL so it stays reasonably fresh. Redis in front of the database usually.

Senior answer

I start from how wrong the data is allowed to be, because that decides everything else. Something a user just edited needs to be right immediately, so that is invalidation on write, not a TTL. A dashboard aggregate can be a minute stale and nobody notices, so that is a TTL and it is simple. Where I am careful is the stampede, since when a hot key expires every request goes to the database at once, which is a fun outage. Either you serve stale while one request refreshes, or you lock the refresh.

Cache stampede is the follow-up. It is also the thing that turns a caching layer into an incident, which is why it gets asked.

7. Testing

What is your testing strategy?

Junior answer

Unit tests for functions, integration tests for how the pieces fit, and end to end for the critical user flows. Aim for good coverage.

Senior answer

I want most of my confidence coming from tests that exercise real boundaries, so integration tests over the actual database rather than a mocked one, because most of the bugs I have shipped lived in that boundary. Unit tests for genuine logic, and a very small number of end to end tests over the flows that lose money if they break, because those are slow and flaky and a suite people ignore is worse than no suite. Coverage percentage I mostly do not care about, it is easy to hit eighty percent while testing nothing that matters.

Pushing back on coverage as a metric, with a reason, reads as experience. Doing it without a reason reads as contrarianism.

8. Storing passwords

How do you store user passwords?

Junior answer

Hashed and salted, never in plain text. Using bcrypt rather than something like MD5 or SHA-256.

Senior answer

With a deliberately slow, memory-hard hash, so bcrypt or argon2, with the work factor tuned to what the login path can afford and revisited as hardware gets cheaper. The point of slowness is that offline cracking after a dump is the real threat model, not the hash itself. Salts are per user and stored with the hash, that is what stops one rainbow table covering everyone. I would also make the failure path constant time, because a login that is measurably faster for unknown users leaks which accounts exist.

The timing side channel is not obscure trivia. It is the marker of someone who thinks about attackers rather than about checklists.

What all eight have in common

Read the senior column again and the pattern is repetitive, almost formulaic.

  • A mechanism. Not what it does, what is happening underneath that makes it do that.
  • A specific instance. "On the last page I did this for" beats "generally you would", every time.
  • A volunteered cost. Every choice loses something. Saying what you gave up is the clearest seniority signal there is.
  • Something measurable. What you would look at to know if you were right.

Notice what is absent. No extra length. No jargon the junior answer lacked. No claim to have worked somewhere impressive. Several senior answers are barely longer than their junior counterpart, and one is shorter.

Interviewers are not listening for the definition. They are listening for whether you have been on the other side of the definition when it went wrong.

How to convert your own answers

Take an answer you would give today and run it through four questions. What is happening one level down. When did I actually see this. What did it cost. How would I know. If you cannot answer the second one for a topic on your CV, that topic is a risk, and it is better to find that out tonight than in the interview.

The uncomfortable part is that you cannot do this by reading. You find your walls by being asked, out loud, and then being asked again about whatever you just said. That is the whole reason the follow-up question exists, and it is the thing worth practising.

Common questions

What if I have not worked on systems big enough to have these stories?

Use the scale you actually had and be honest about it. A specific story about a small system reads far better than a vague story about a large one, and interviewers can tell the difference immediately.

Should I volunteer the trade-off even if they did not ask?

Yes. It is the highest-value sentence in most answers, and it usually steers the follow-up onto ground you have already thought about.

Does this apply to junior interviews too?

The mechanism and the measurement parts do. Nobody expects a junior to have production war stories, but explaining what is happening underneath is available to anyone at any level.

SeniorityInterview techniqueExamples

Keep reading

All posts