Skip to content
Search lessons, topics, tests…
Esc

    ↑ ↓ moveEnter openEsc close

    Guided course · Testing and Production Engineering

    Production-Grade .NET Backend Interview Path: back to the course

    Module 6 · Practice and Assessment · Lesson 9 of 9

    Final Mock Interview: 25 Questions on Production-Grade .NET Backend Engineering

    How this mock works

    • 25 questions in four parts: 10 multiple choice, 5 spot-the-problem, 5 production scenarios, 5 "explain it to the interviewer" questions.
    • Timed mode: 35 minutes. Write your answers first, then open the answer key below.
    • Online version: Mock Interview: Production-Grade .NET Backend: 25 questions, 35 minutes, scored automatically (Full Access).
    • Scoring: MCQ 1 point each (10). Every other question is scored up to 5: 2 for naming the problem correctly, 2 for a correct fix, 1 for a trade-off or caveat (75). Total 85. Pass: 60 (about 70%).
    • Assume ASP.NET Core and EF Core on .NET 10 with SQL Server unless a question says otherwise. Each answer links back to its lesson.

    Part A: multiple choice (1 point each)

    A1. A singleton that captures a scoped service is: a) fine if the service is stateless b) a captive dependency c) a transient leak d) required for caching

    A2. With ValidateOnBuild enabled, which additional setting checks singleton-to-scoped capture at startup? a) ValidateOnBuild only b) ValidateScopes c) AddOptions d) UseDeveloperExceptionPage

    A3. An ASP.NET Core action's CancellationToken parameter is bound to: a) host shutdown b) HttpContext.RequestAborted c) HttpClient.Timeout d) a new token per call

    A4. HttpClient.Timeout is 100 s and a linked per-operation cancellation budget is 2 s. Which configured budget expires first? a) 100 s b) 2 s c) 102 s d) whichever started first

    A5. Offset pagination with a large Skip is slow because: a) EF cannot translate Skip b) the database still reads and discards earlier rows c) it disables indexes d) it tracks every row

    A6. Without a unique constraint, "check the idempotency key, then insert" allows: a) nothing bad b) two concurrent requests to both execute c) a deadlock every time d) only one request to run

    A7. A token bucket limiter: a) forbids bursts b) allows bursts up to the bucket size with an average refill rate c) limits concurrency d) queues without limit

    A8. An unbounded request queue under a traffic spike converts overload into: a) faster responses b) memory pressure and stale work c) fewer retries d) lower latency

    A9. In the half-open state, a circuit breaker: a) blocks all calls b) lets limited probe calls through c) retries every call d) resets its counters forever

    A10. A blameless review: a) assigns no owners b) removes accountability c) studies decisions in context and assigns owned actions d) skips the timeline

    Part B: spot the problem (up to 5 points each)

    B1.

    C#
    builder.Services.AddSingleton<ReportCache>();     // ReportCache(ITenantContext tenant)
    builder.Services.AddScoped<ITenantContext, HttpTenantContext>();

    B2.

    C#
    public async Task<Quote> GetQuote(CancellationToken ct)
    {
        var price = await pricing.GetAsync(CancellationToken.None);
        var stock = await inventory.GetAsync(CancellationToken.None);
        return new Quote(price, stock);
    }

    B3.

    C#
    var orders = await db.Orders.ToListAsync(ct);
    var rows = orders.Where(o => o.TenantId == tenantId)
                     .Select(o => new { o.Id, o.Customer.Name })   // lazy loading on
                     .ToList();

    B4.

    C#
    await strategy.ExecuteAsync(async () =>
    {
        await using var tx = await db.Database.BeginTransactionAsync(ct);
        db.Payments.Add(payment);
        await db.SaveChangesAsync(ct);
        await gateway.ChargeAsync(payment.Amount, ct);   // third-party call
        await tx.CommitAsync(ct);
    });

    B5.

    C#
    services.AddHttpClient<CatalogClient>()
        .AddResilienceHandler("catalog", b => b
            .AddRetry(new() { MaxRetryAttempts = 5, Delay = TimeSpan.FromSeconds(2), BackoffType = DelayBackoffType.Constant })
            .AddTimeout(TimeSpan.FromSeconds(10)));
    // gateway and SDK also retry 3 times

    Part C: production scenarios (up to 5 points each)

    C1. After a release, p95 order-write latency rises from 300 ms to 4 s while throughput and CPU are flat. Walk through your diagnosis loop and the likely fix.

    C2. Clients retry POST /orders after timeouts and some customers are charged twice. Design the idempotency contract end to end.

    C3. One free-tier tenant's batch job floods a shared API and paid tenants time out. Design the protection.

    C4. A recommendations dependency fails and checkout latency climbs because every request waits on it. Compose timeouts, retries and a breaker so checkout stays healthy.

    C5. You run the post-incident review for a cascading timeout incident. What does the document contain, and what makes its action items good?

    Part D: explain it (up to 5 points each)

    D1. Explain the three built-in DI lifetimes and the failure each one invites.

    D2. Explain how you classify an OperationCanceledException.

    D3. Explain when to choose projection, Include or AsSplitQuery.

    D4. Explain how metrics, traces and logs each help during an incident.

    D5. Explain what closure evidence for an action item looks like.


    Answer key with explanations

    A1: b. The longer-lived component captures a shorter-lived one, so request data or a DbContext is shared across requests and threads. (Lesson 1)

    A2: b. ValidateScopes checks the capture; ValidateOnBuild runs graph validation during provider construction, including scope validation when both are enabled. Enable both in development and integration tests. (Lesson 1)

    A3: b. Pass it through every layer instead of replacing it with CancellationToken.None. (Lesson 2)

    A4: b. The 2-second budget requests cancellation first; cooperative cancellation does not guarantee completion at exactly 2 seconds. Per-operation linked tokens are more expressive than the client-wide timeout. (Lesson 2)

    A5: b. Prefer keyset pagination with a unique ordering and a matching index for deep pages. (Lesson 3)

    A6: b. Both see no record and both execute. The unique constraint is the final concurrency guard. (Lesson 4)

    A7: b. It permits controlled bursts while enforcing an average rate. (Lesson 5)

    A8: b. Every queued request costs memory and may expire before it runs. Keep queues small and reject excess work explicitly. (Lesson 5)

    A9: b. After the break duration a limited probe tests recovery; success closes the circuit, failure reopens it. (Lesson 6)

    A10: c. Blameless is not consequence-free: actions still have owners, dates and follow-up. (Lesson 8)

    B1. Captive dependency: the singleton retains a root-scoped tenant context across requests, rather than receiving each request's scoped context. Fix: pass the tenant key into each call (and into cache keys), or make the cache scoped if it holds request state. ValidateScopes together with ValidateOnBuild catches this during provider construction. (Lesson 1)

    B2. The request token is dropped, so work continues after the client leaves, and the calls run in sequence with no shared budget. Fix: pass ct (linked with a local deadline) to both calls, start them in parallel and await both, and classify caller cancellation separately from timeouts. (Lesson 2)

    B3. ToListAsync loads every order of every tenant into memory, then filters on the client, and lazy loading can issue an additional customer query per order (N+1). Fix: filter and project in SQL, db.Orders.Where(o => o.TenantId == tenantId).Select(o => new { o.Id, o.Customer.Name }).ToListAsync(ct), and turn lazy loading off for API reads. (Lesson 3)

    B4. A third-party charge inside a replayable transaction: a transient failure replays the block and can charge twice, and a database rollback cannot undo the charge. Fix: commit the payment intent (and an outbox row) first, charge outside the retried block with the provider's idempotency key, record the provider operation ID, and reconcile uncertain outcomes. (Lessons 4, 6)

    B5. Constant 2-second delays with no jitter synchronize retries, and the 10-second timeout here is per attempt, so with 5 retries the total far exceeds any user budget. Gateway and SDK retries multiply the calls again (up to 6 x 4 x 4 = 96 dependency calls for one user action here). Fix: one retry owner, few attempts with exponential backoff and jitter, a per-attempt timeout inside a total deadline, and retries only for transient failures. (Lesson 6)

    C1. State the symptom and scope, check what changed (the release), form one hypothesis and the cheapest signal that can disprove it. Traces show SQL duration unchanged but time spent before the database span; the connection pool is pinned at its limit. A new HTTP call inside a transaction holds connections longer. Mitigate by rolling back or feature-disabling the path (do not just enlarge the pool), then fix by moving the call outside the transaction and adding a pool acquisition-time metric. (Lesson 7)

    C2. The client sends a unique key per logical operation and reuses it on retries. The server stores tenant, operation, key, request hash, status and response in the same transaction as the order, with a unique constraint. A repeat returns the stored response; a different hash is a conflict; an in-progress record returns 409 or 202 with a status URL. The charge uses the provider's idempotency key. Test concurrent duplicates and a timeout after commit. (Lesson 4)

    C3. A partitioned limiter keyed by tenant (and workload class), with separate budgets for free and paid tiers, small queue limits, fast 429 responses with a stable Retry-After, and limits on scheduled jobs and consumers that share the database too. Monitor accepted and rejected requests per partition, queue wait and downstream saturation. (Lesson 5)

    C4. Give the recommendations call a short per-attempt timeout inside checkout's total budget, at most one retry for transient failures, and its own circuit breaker so failures open fast. Serve the page without recommendations as a documented fallback and measure how often the fallback is used. Keep payments and inventory on separate breakers. (Lesson 6)

    C5. An executive summary, impact, an evidence-based timeline (observations, decisions, hypotheses, unknowns), a technical narrative of trigger, propagation, detection and mitigation, a response analysis including what worked, prioritized prevent/detect/contain/recover/learn actions, evidence gaps and follow-up. Good actions name the risk, the concrete change, an owner, a due date and closure evidence. (Lesson 8)

    D1. Transient: a new instance per resolution; not automatically thread-safe and can be expensive. Scoped: one per scope (per request in ASP.NET Core); fits DbContext, but breaks if captured or used outside a scope. Singleton: one per container; must be thread-safe and must not hold request data, a DbContext or tenant identity. (Lesson 1)

    D2. If the caller's token is cancelled, the client gave up. If only a local budget token fired, it is a timeout. If the host stopping token fired, it is shutdown. None should be logged as an application error, and the original exception should be preserved as the inner exception when translating. (Lesson 2)

    D3. Entity-free DTO projection for API read models: only the needed columns, aggregates in SQL, no tracking. Include when you need a tracked entity graph for domain behaviour. AsSplitQuery when several collections cause a cartesian explosion, accepting extra round trips and possible inconsistency between the queries. Check the SQL either way. (Lesson 3)

    D4. Metrics detect and bound the problem (rates, errors, percentiles, pool usage, queue age). Traces follow one slow request and show which stage dominates, compared with a healthy trace. Logs explain decisions and failures with stable structured fields such as operation, tenant, attempt and trace ID. Return to metrics to estimate impact. (Lesson 7)

    D5. The intended control exists and was verified: a test result, a dashboard showing the new alert firing in a controlled fault, a game-day record or a successful rollback drill, reviewed by someone else with a date. Merged code alone is not closure. (Lesson 8)

    Score bands

    • 75-85: ready for senior production-backend questions.
    • 60-74: pass. Review the lessons linked from the answers you missed.
    • Below 60: redo the lesson practice tests and retake this mock in a week.

    Analogy

    Everyday picture

    A game day

    The mock is a game day for your interview answers: captive services, lost cancellations, retry storms and a postmortem, run against a clock in a safe place, so the real interview feels like a second run.

    Quick reference

    Where each answer lives

    • DI lifetimes and captive dependencies: Lesson 1.
    • Cancellation and timeout budgets: Lesson 2.
    • EF Core query performance: Lesson 3.
    • Idempotency and transactions: Lesson 4.
    • Rate limiting and backpressure: Lesson 5.
    • Timeouts, retries and breakers: Lesson 6.
    • Incident diagnosis: Lesson 7.
    • Post-incident reviews: Lesson 8.
    Sign in to mark lessons done and keep your place in the course.Sign in