
Ask a .NET candidate about dependency injection lifetimes and they will give you the right answer. Transient, scoped, singleton, one line each.
Then ask what happens when a singleton captures a scoped service. Most people have read that it is bad. Far fewer have watched the result: a DbContext shared across requests, errors about concurrent use that appear only under load, and a bug that nobody can reproduce on a laptop.
That gap is the whole problem with .NET interviews. The framework does so much for you that the expensive mistakes are not visible in the code being reviewed. They are in the lifetime, the synchronization context, the query that looks like a list, and the background service that stopped three weeks ago without telling anyone.
Because it is the one piece of .NET knowledge that is both framework specific and easy to grade. It is also fair: a candidate who cannot name the three lifetimes has not used the framework seriously.
But naming them is where it stops being useful. The interesting question is not what scoped means, it is what you do when a request fails at 200 concurrent users and works perfectly at one. So hand the candidate a service that does exactly that.
The setup. An endpoint that calls an async method and blocks on it, with .Result or .Wait(). It behaves fine in development. Under concurrency, requests start timing out while the machine looks idle.
Ask: this endpoint times out under load and the CPU is flat. What is happening?
Strong looks like: they notice the blocking call and can explain why a flat CPU with rising latency points at waiting rather than work. They know the fix is to make the path async all the way, not to add threads, and they can say what else in the call chain has to change for that to be possible.
Weak looks like: raising the thread pool minimum and calling it solved. It will often make the symptom go away in a load test, which is exactly why it is a dangerous answer: it buys silence rather than understanding.
Why it works: sync-over-async is the most common .NET production failure that passes code review, because each line looks reasonable on its own.
The setup. A singleton service that was given a scoped dependency at construction. It works until two requests arrive at once, then produces errors about concurrent use, or worse, quietly returns data belonging to another request.
Ask: two users reported seeing each other's data. Start wherever you like.
Strong looks like: they treat it as a correctness and security problem, not a performance one. They look at registration before they look at the business logic, and they know that a captured dependency is a lifetime bug rather than a threading bug. They talk about how to prevent the class of mistake, with a factory or scope-aware resolution, and they ask whether anything was logged that would tell them how long this has been happening.
Weak looks like: adding a lock. The errors stop, the data crossing between requests does not.
Why it works: cross-request data leaks are the kind of incident you have to disclose. Watching someone reason about one tells you how they will behave on the worst day.
The setup. An endpoint using Entity Framework that materialises far more than it needs: change tracking left on for a read-only path, a filter applied in memory rather than in the database, or an include chain that pulls in half the schema. It is fine against the seed data and bad against a realistic table.
Ask: this page takes six seconds against production-sized data. Make it fast and tell me what you changed.
Strong looks like: they look at the generated SQL rather than guessing, which is the single habit this task is testing. They can tell the difference between a query that is slow and a query that is fetching too much. They reach for AsNoTracking, projection to exactly the fields the page needs, and filtering at the database, and they re-measure to prove it.
Weak looks like: adding a cache in front of it, or rewriting it in raw SQL without ever reading what the ORM was emitting.
Why it works: ORMs hide cost by design. Whether somebody looks through that abstraction under pressure is most of what separates a senior .NET engineer from a productive junior one. We go deeper on the same instinct in the database performance interview.
The setup. A background service that processes a queue. An unhandled exception took it down weeks ago. The web application is healthy, the health check is green, and the queue is growing.
Ask: customers say their exports never arrive, but the site is up. Go.
Strong looks like: they check whether the worker is running before reading its code. They find the swallowed exception, and they fix both halves: the error itself and the fact that nobody was told. They propose a health signal that covers background work, because a liveness probe on the web process says nothing about a worker that has stopped.
Weak looks like: restarting the service. The queue drains, everyone relaxes, and it happens again on a date nobody will connect to this conversation.
Why it works: silent background failure is the most under-interviewed failure mode in any framework that makes background work easy, and .NET makes it very easy.
Four rows, the same for every candidate:
None of those rows is "knows the three lifetimes". That turns up for free in task two, where it decides the answer.
Every one of these needs a running system: a load generator, a realistic table, a queue with a backlog, a process that is genuinely stopped. A snippet in a document cannot show a flat CPU next to rising latency.
EasyEnv gives the candidate a real Linux box with the .NET service deployed, the blocking call already in place, the singleton already capturing, the table already large and the worker already dead. The recipe is the same for every candidate, so comparison is fair and the bugs do not drift. Boxes are disposable, so full access costs nothing. Sessions are recorded, terminal and screen, so you can see whether they read the SQL or guessed at it.
The limits, plainly. This runs 45 to 60 minutes and belongs after a short screen, not instead of one. The review needs someone who knows .NET well enough to tell a good shortcut from a lucky one. And if the role is mostly greenfield services with no production ownership, task four is more than the job asks for.
Stop asking what scoped means. Give the candidate a deadlock under load, a singleton holding something it should not, a query that drags a table into memory, and a worker that died without telling anyone.
If you only run one, run the captured dependency. How a candidate reacts to two users seeing each other's data tells you more about their judgment than any question about lifetimes.
Run live coding sessions and take-home challenges in real production environments. Watch sessions back, score consistently, and hire with confidence.
More posts you might like
Node interviews ask candidates to recite the event loop, then production fails in ways no recitation covers. Here are four tasks on a running service, an unhandled rejection, a heap that only climbs, a retry storm and a blocked event loop, with what separates a strong answer from a plausible one.
Java interviews still run on trivia that a study guide covers in an evening, while the expensive problems live in transaction boundaries, pool exhaustion and upgrades nobody dares start. Here are four tasks on a running Spring service, and what each one separates.
Read moreMost teams describe their AI adoption as "we're figuring it out," which is not a stage anyone can plan against. Here is a five-stage maturity model for AI literacy across an engineering team, how to tell which one you are actually in, and what moves you up one.
Read more