All jobs / Backend developer

Performance interview questions

Performance questions a Backend developer can be asked, from first job to senior. Open one to see the strongest answer and why it works.

  1. A teammate opens a pull request that rewrites a service “for performance”. What do you ask for before you approve it?

    The strongest answer: A number: what was slow, measured where the user waits, what the profile or trace says is spending the time, and the same measurement after the change so we can see it helped

    Why it works: Exactly — a before, a cause and an after. Without the cause you are optimising the part that was never the problem.

    • Measure, change, measure again.
    • Start where the user waits.
    • Profile before you guess.
    • Keep the before and after numbers.
  2. The interviewer asks: “The API is idle but requests hang on 'opening a connection.' What's going on?” Strongest answer?

    The strongest answer: The pool is exhausted — every connection is checked out, often by slow queries or a leak (missing dispose). I check pool metrics, active queries, and that every request releases the connection.

    Why it works: Exactly. Hanging on the pool is a symptom: held connections or too much concurrency into a small pool. Fix the leak and the slow query; then size the pool to the database, not to hope.

    • Hangs here often mean a starved pool.
    • Find leaks and slow queries first.
    • Size the pool to the database, not to infinity.
    • Don't turn pooling off.
  3. The interviewer asks: “The API returns 429. What should a client do?” Strongest answer?

    The strongest answer: Back off. Honour Retry-After if present, then retry with jitter. Don't tight-loop, and don't silently switch to another user's credentials to “get more quota.”

    Why it works: Exactly. 429 is a contract: you are too fast. Retry-After is the polite signal. Gateways and browsers already understand it.

    • 429 means slow down.
    • Honour Retry-After; add jitter.
    • Cap retries; then tell the user.
    • Don't loop forever.
  4. The interviewer asks: “You add Cache-Control to a JSON API. What do you watch for?” Strongest answer?

    The strongest answer: Authenticated or user-specific responses get private (or no-store). public + a CDN will leak another user's body. I set a max-age I can live with, and I use ETags when I need revalidation.

    Why it works: Exactly. Shared caches are the danger. private / no-store for personal data; public only for truly public, versioned representations.

    • User-specific → private or no-store.
    • public only for truly public data.
    • ETag/max-age with a plan to invalidate.
    • Never CDN-cache another user's JSON.
  5. The interviewer asks: “Why is OFFSET 100000 slow even with a LIMIT 20?” Strongest answer?

    The strongest answer: The database still walks and discards the first 100000 rows in the ordered index. Cost grows with the offset. A cursor on the last (sort key, id) seeks instead of skipping.

    Why it works: Exactly. OFFSET is “skip N.” Deep pages scan N. Keyset pagination is O(log n) with the right index.

    • OFFSET still reads the skipped rows.
    • Deep pages want keyset/cursors.
    • Index the (sort, id) tuple.
    • Don't drop ORDER BY to “go faster.”
  6. The interviewer asks: “Where do you enforce rate limits?” Strongest answer?

    The strongest answer: At the edge (gateway / mesh) by identity — user, API key, IP as a last resort — with a budget the app can also enforce on expensive routes. Limits are a product and a safety control, not a random 429.

    Why it works: Exactly. Edge limits protect everyone; app limits protect a heavy endpoint (export, search). Key by principal or you punish a NAT.

    • Limit at the edge by identity.
    • Extra caps on expensive routes.
    • Don't use one shared counter for all tenants.
    • Don't confuse scale-out with a security control.
  7. The interviewer asks: “This query is slow in production but fine on your laptop. What do you do first?” Strongest answer?

    The strongest answer: Get the production plan (EXPLAIN ANALYZE / actual plan from the store). Compare row estimates, look for seq scans, nested loops on big sets, and stale stats — don't rewrite blindly.

    Why it works: Exactly. The laptop has different stats, cache and data shape. The plan is the artefact. Then you fix stats, the predicate, or one index.

    • Read the production plan first.
    • Estimates vs actuals, scans vs seeks.
    • Fix stats/predicate/index — not folklore.
    • Don't copy prod PII onto a laptop.
  8. An endpoint takes 1.5 seconds at p95. The container’s CPU sits at 8% and the CPU profiler shows almost nothing.

    What do you look at next?

    The strongest answer: Wall-clock time rather than CPU: a trace or timed spans across the request showing how long it waits on the database, on downstream calls and on the connection pool — low CPU with high latency means waiting

    Why it works: Exactly — a CPU profiler cannot see waiting. Spans across the whole request show where the time actually goes.

    • Low CPU means waiting.
    • Measure wall clock, not CPU.
    • Trace the spans across the request.
    • Find out what it waits on.
  9. The interviewer asks: “The cache expires and 2,000 requests hit the database at once. How do you prevent that?” Strongest answer?

    The strongest answer: Stampede control: single-flight / lock around the rebuild, jittered TTLs, and a stale-while-revalidate window so only one request (or a small set) refills. Don't let every waiter run the same query.

    Why it works: Exactly. The thundering herd is a known pattern. Jitter plus a rebuild lock is the standard fix; serving stale briefly is allowed if the product can live with it.

    • One rebuild, many waiters.
    • Jitter TTLs; consider SWR.
    • Don't drop the cache as the fix.
    • Don't cache the entire database.
  10. Under load, someone raises the connection pool from 20 to 200 per instance. Throughput drops and p95 gets worse.

    Why?

    The strongest answer: The database has fixed CPU and disk; two hundred sessions per instance contend instead of finishing work, so queueing at the pool beats queueing inside the engine — fix the slow queries and long transactions

    Why it works: Exactly — the pool is admission control, and a queue in front of a saturated resource is a feature rather than a bug.

    • The pool is admission control.
    • A saturated engine gets slower, not faster.
    • Size the pool to the database.
    • Shorten transactions and queries instead.
  11. The interviewer asks: “How do you know we can take 3× traffic on Monday?” Strongest answer?

    The strongest answer: From the SLI and a load test that matches the mix: p95 latency, error ratio, pool saturation, dependency budgets. I find the first bottleneck (DB CPU, lock, payload size), not a guess from last year's peak.

    Why it works: Exactly. Capacity is a measured bottleneck. Horizontal pods won't save a serialised table or a single-threaded dependency.

    • Load-test the real mix against SLIs.
    • Name the first bottleneck.
    • Pods don't fix a hot database.
    • Autoscale is a mechanism, not a forecast.
  12. The interviewer asks: “The bill doubled but QPS didn't. Where do you look?” Strongest answer?

    The strongest answer: Cost per request: heavier queries, a new N+1, a wider SELECT, a chatty service hop, an unbounded fan-out, or a retry storm. I look at traces and DB time, not just request count.

    Why it works: Exactly. Capacity and money follow work per request. A 10× slower SQL at the same QPS is a 10× database bill.

    • Bill follows work per request.
    • Traces and DB time, not only QPS.
    • Fan-out and retries hide in “flat QPS.”
    • Don't drop indexes to save money blindly.