AnyLearn
All interview prep
EngineeringMid-levelBackend Engineer

Backend Engineer Interview Prep: Questions and a Mock Test

Backend interviews are the most standardised of any engineering loop, which cuts both ways: the format holds few surprises, and everyone has practised the same things. What separates candidates is not the coding round, where most competent people converge, but the rounds where a design has to be defended and a specific mechanism has to be explained precisely. This page covers what those rounds test, the details that reliably catch people out, and ends with a graded mock across six areas.

The loop

How the process is structured

The interview loop: each round, how long it runs, and what it tests
RoundLengthWhat it tests
1.Coding[1]Not publishedData structures, algorithms and clean implementation under time pressure, usually one or two problems. Assessed on correctness, complexity reasoning, edge case handling and whether you communicate while working rather than in silence.
2.API and data modelling[1]Not publishedDesigning endpoints and the schema behind them. HTTP method semantics are examinable here: RFC 9110 defines idempotent methods as those where "the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request", which is why retry-safe creates need an idempotency key.
3.System design[2]Not publishedAn open-ended design with scale numbers. Requirements first, then a shape, then the tradeoffs. Operational concerns are usually probed through the twelve-factor lens: stateless processes, config in the environment, and "fast startup and graceful shutdown".
4.Debugging and past work[3]Not publishedA production problem to reason about, plus depth on something you built. Expect database behaviour under concurrency, caching decisions and their invalidation, and whether you can explain a system you worked on to someone who has never seen it.

Bracketed markers point to the dated sources at the end of this article. Loops change; check the retrieval dates before relying on a round count.

HTTP semantics are examinable, and often assumed

API design rounds test whether you know what the protocol already promises, because designing on top of a contract you have half-remembered produces subtle bugs.

RFC 9110 defines the two properties that matter most. A method is safe if "their defined semantics are essentially read-only; that is, the client does not request, and does not expect, any state change on the origin server". GET, HEAD, OPTIONS and TRACE are safe. A method is idempotent if "the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request". That set is the safe methods plus PUT and DELETE.

Two consequences come up constantly. First, POST is deliberately not idempotent, so any create operation that a client might retry needs an idempotency key supplied by the caller, since the protocol will not save you. Second, DELETE is idempotent even though the second call usually returns 404, because the specification is about the effect on server state rather than the status code: after a DELETE, "subsequent DELETE requests to that resource will likely result in a 404 (Not Found) status code, which accurately reflects the fact that the resource is no longer available".

Status codes are worth getting right too. 401 means unauthenticated, 403 means authenticated but not permitted, 409 signals a conflict with current state, and 422 is the common choice for a syntactically valid request that fails semantic validation.

Databases: indexes and isolation

Almost every backend loop contains a database question, and almost all of them reduce to two things: what an index can and cannot help with, and what a transaction actually guarantees.

On indexes, the mental model to bring is that a B-tree index is sorted, so it can serve equality and range lookups and can also satisfy an ORDER BY without a sort. That ordering explains the leftmost-prefix rule for composite indexes: an index on (a, b) helps a query filtering on a, or on a and b, but not one filtering on b alone. It also explains why wrapping a column in a function usually disables the index, since the stored order is of the column and not of the function's output. A covering index, one that contains every column the query needs, avoids reading the table at all.

On transactions, be precise about the anomalies rather than reciting the level names. PostgreSQL's documentation is unusually clear here: "you can request any of the four standard transaction isolation levels, but internally only three distinct isolation levels are implemented, i.e., PostgreSQL's Read Uncommitted mode behaves like Read Committed". Its Repeatable Read is stronger than the standard requires, because it does not permit phantom reads. Read Committed, the default, permits both non-repeatable reads and phantoms, which is exactly why a read-modify-write sequence needs either explicit locking or an atomic update.

Caching, and the part everyone forgets

Caching questions are easy to answer badly because the addition is obvious and the removal is not. Interviewers are listening for invalidation.

The usual patterns are worth naming precisely. Cache-aside means the application checks the cache, and on a miss reads the database and populates the cache; it is simple and leaves the cache and database briefly inconsistent. Write-through updates both on every write, keeping them consistent at the cost of write latency. Write-behind acknowledges the write after updating only the cache and flushes later, which is fast and can lose data.

The failure modes have names and are frequently asked about directly. A stampede, sometimes called a thundering herd, happens when a hot key expires and every concurrent request goes to the database at once; the answers are a lock so only one request recomputes, or serving stale data while a single background refresh runs. Cache penetration is repeated lookups for keys that do not exist, which pass straight through; caching a negative result, or a probabilistic filter, is the usual defence.

On eviction, know that a TTL bounds staleness while LRU bounds memory, and that they solve different problems. The senior answer to "how long should the TTL be" is a question back: how stale can this data be before someone is harmed.

Concurrency bugs, in the language of the interview

Concurrency questions in a backend loop are rarely about threads in the abstract. They are about two requests arriving at the same time and doing something incoherent.

The canonical example is the read-modify-write race: two requests read a balance of 100, both subtract 30, and both write 70. The fixes are worth being able to compare. An atomic update performed in the database, expressed as a single statement rather than a read followed by a write, removes the window entirely. Pessimistic locking takes a row lock for the duration of the transaction, which is correct and reduces concurrency. Optimistic concurrency control adds a version column and fails the write if the version changed, which suits low-contention cases and requires the caller to handle a retry.

Deadlock is the other staple. Two transactions taking the same locks in different orders will eventually block each other, and the standard prevention is a globally consistent lock ordering. Databases detect deadlocks and abort one participant, so application code has to be prepared for a transaction that fails and can be safely retried.

Be ready for distributed locks as a follow-up, and for the honest answer that they are harder than they look: a lease can expire while the holder still believes it holds the lock, so the protected operation ideally needs to be idempotent or fenced by a monotonically increasing token.

Design rounds want constraints, then a shape

System design rounds go wrong in a predictable way: the candidate starts drawing boxes before establishing what the system must do. The strong opening is to extract functional requirements, then scale numbers, then the quality attributes that actually drive the design.

Estimation is a tool, not a ritual. Deriving reads per second from daily active users and a usage assumption tells you whether the design needs a cache at all, and deriving storage growth per year tells you whether partitioning is a day-one concern or a year-three one. Stating the assumption out loud so the interviewer can correct it is part of the exercise.

The twelve-factor methodology remains a good shared vocabulary for the operational half. It asks you to "store config in the environment", to "treat backing services as attached resources" so a database can be swapped without a code change, to "execute the app as one or more stateless processes", and to "maximize robustness with fast startup and graceful shutdown". Statelessness is the one that does the most work in an interview, because it is what makes horizontal scaling, rolling deploys and autoscaling possible, and it is why session state belongs in a shared store.

Finally, expect to be pushed on a tradeoff you proposed. The answer that scores is not the one with no downsides; it is the one where you already know what the downsides are.

Open-ended

What they actually ask

  1. 1.Design a URL shortener. Start wherever you like.

    What a strong answer covers

    The opening move matters more than the design. Strong candidates extract requirements first: custom aliases or not, expiry, analytics, and the read-to-write ratio, which for this system is overwhelmingly read-heavy and therefore decides everything downstream. Then key generation, and the tradeoff between hashing with collision handling and a counter encoded in base62, where the counter is simpler and predictable and the hash is neither. Then storage, a key-value shape rather than a relational one; then caching, since the working set of popular links is small; then the redirect status code, 301 being cacheable and therefore invisible to analytics, 302 being the usual choice for that reason. Capacity estimates should be stated as assumptions, and the design should follow from them rather than preceding them.

  2. 2.A payment endpoint occasionally charges customers twice. What is happening and how do you fix it?

    What a strong answer covers

    The likely cause is a retried POST, from a client, a proxy, a load balancer, or an impatient user, against an operation the protocol does not make idempotent. Strong answers reach for a client-supplied idempotency key stored with a unique constraint, so the second attempt returns the original result rather than performing the work again, and they say what happens to a retry that arrives while the first is still in flight. The subtle half is the boundary: the key has to be recorded in the same transaction as the effect, or a crash between the two reintroduces the bug. Mentioning that the external payment provider also needs an idempotency key, since your database transaction cannot roll back their side, is a strong signal.

  3. 3.A query that used to return in 10ms now takes 4 seconds. Nothing about the query changed. Where do you look?

    What a strong answer covers

    Expected first move is the execution plan, not speculation, and comparing it against what it used to do. Causes worth enumerating: the table grew past the point where the planner switched from an index scan to a sequential scan, statistics went stale so the planner is estimating badly, an index was dropped or invalidated, parameter values changed such that a cached plan no longer suits, lock contention from another workload, or bloat from heavy updates leaving dead tuples. Strong candidates distinguish a plan problem from a contention problem early, since the diagnostics differ, and they check whether the slowdown is on every execution or only on some, which points toward parameter-dependent plans.

  4. 4.When would you not use a cache?

    What a strong answer covers

    This tests whether caching is a reflex or a decision. Reasons not to cache: the data changes more often than it is read, so the hit rate will be low and invalidation constant; correctness requires the current value, as with an account balance at the moment of a transfer; the underlying query is already fast and the cache adds a network hop plus a new failure mode; or the working set has no hot keys, so the cache is memory spent on a uniform distribution. Strong answers also raise the operational cost: a cache is another system that can fail, and a service that cannot survive its cache being empty has made the cache a dependency rather than an optimisation. Testing cold-start behaviour deliberately is a mature thing to mention.

  5. 5.Two services need to stay consistent, but they have separate databases. How do you handle that?

    What a strong answer covers

    The expected recognition is that a distributed transaction across two services is usually the wrong tool, and that the honest options trade atomicity for eventual consistency. Strong answers describe the outbox pattern, writing the domain change and the event to be published in one local transaction, then relaying the event asynchronously, which turns a two-system problem into a one-system one. They cover the saga shape for multi-step workflows, with compensating actions rather than rollback, and they note that consumers must be idempotent because at-least-once delivery is the realistic guarantee. The best answers ask first whether the split is right at all, since two things that must change atomically are often evidence of a boundary drawn in the wrong place.

  6. 6.How would you add a NOT NULL column to a table with 200 million rows, with no downtime?

    What a strong answer covers

    This is a favourite because it tests operational judgement rather than syntax. Strong answers make the change multi-phase: add the column as nullable with no default rewrite, backfill in bounded batches with pauses so replication and lock contention stay manageable, deploy application code that writes the column for every new row, verify no nulls remain, and only then add the constraint. They understand which operations take a long-held exclusive lock in their specific database and which do not, mention that a lock queued behind a long transaction blocks every subsequent query on that table, and set a lock timeout so a failed attempt fails fast rather than stalling production. The expand-and-contract framing, where old and new schema coexist so deploys and rollbacks stay possible, is the underlying principle.

Worked examples

Three sample questions, answered

These three show the level the mock is pitched at, with the answer and the reasoning in the open. The graded paper keeps its answer key server-side.

1.According to RFC 9110, which of these methods is idempotent but not safe?
HTTP semantics and API design
  • GET
  • POST
  • HEAD
  • DELETE

Why: Safe methods are read-only in intent: GET, HEAD, OPTIONS and TRACE. Idempotent methods are those where repeating the request has the same effect as making it once, which is the safe set plus PUT and DELETE. DELETE changes state, so it is not safe, but deleting twice leaves the same state as deleting once, so it is idempotent. POST is neither.

2.You have an index on (customer_id, created_at). Which query can use it efficiently?
Indexes, transactions and isolation
  • WHERE created_at > '2026-01-01'
  • WHERE customer_id = 42 AND created_at > '2026-01-01'
  • WHERE UPPER(customer_id) = 'X'
  • WHERE created_at > '2026-01-01' ORDER BY customer_id

Why: A composite index is sorted by its first column, then the second within it, so it can be used only when the leftmost columns are constrained. Filtering on customer_id and then narrowing by created_at matches that order. Filtering on created_at alone cannot use it, and wrapping a column in a function makes the stored ordering inapplicable.

3.A popular cache key expires and 5,000 concurrent requests all miss and hit the database. What is this called, and what fixes it?
Caching strategies and invalidation
  • Cache penetration, fixed by caching negative results
  • Cache pollution, fixed by increasing the cache size
  • A cache stampede, fixed by a lock so only one request recomputes, or by serving stale while refreshing
  • Write amplification, fixed by switching to write-behind

Why: A stampede, or thundering herd, is many concurrent misses on the same newly-expired key. The standard mitigations are single-flight, where one request recomputes and the rest wait, and stale-while-revalidate, where the expired value is served while one background refresh runs. Cache penetration is a different problem: repeated lookups for keys that never existed.

The mock

An 18-question knowledge check

This is a knowledge check, not a simulation. The real loop happens on a whiteboard, in an editor, and in conversation. What this paper does measure is the underlying knowledge those rounds draw on: each question is tagged with a topic, grading happens per topic, and a weak topic points you at the course that fixes it.

Your paper0 / 18 answered
  1. 1.A client retries a POST that creates an order because the response timed out. What does HTTP guarantee about the outcome?
    HTTP semantics and API design
  2. 2.Which status code is correct for a request that is well-formed and authenticated, but whose contents fail a business rule?
    HTTP semantics and API design
  3. 3.What is the practical difference between PUT and PATCH?
    HTTP semantics and API design
  4. 4.In PostgreSQL's default isolation level, which anomaly can still occur?
    Indexes, transactions and isolation
  5. 5.Why does a query filtering on WHERE DATE(created_at) = '2026-08-13' typically fail to use an index on created_at?
    Indexes, transactions and isolation
  6. 6.What does a covering index achieve?
    Indexes, transactions and isolation
  7. 7.In the cache-aside pattern, what happens on a read miss?
    Caching strategies and invalidation
  8. 8.A service caches lookups for user IDs. An attacker requests thousands of IDs that do not exist. What is the failure, and the fix?
    Caching strategies and invalidation
  9. 9.Why is adding random jitter to cache TTLs worthwhile?
    Caching strategies and invalidation
  10. 10.Two concurrent requests each read a counter of 100, add 1, and write 101. What is the cleanest fix?
    Concurrency and race conditions
  11. 11.What does optimistic concurrency control require the caller to handle that pessimistic locking does not?
    Concurrency and race conditions
  12. 12.Two transactions deadlock. What is the standard prevention, and what must the application do regardless?
    Concurrency and race conditions
  13. 13.In CAP terms, what does choosing availability during a network partition mean in practice?
    Distributed systems and consistency
  14. 14.What problem does the outbox pattern solve?
    Distributed systems and consistency
  15. 15.Why is a distributed lock based on a key with an expiry not sufficient on its own for a critical section?
    Distributed systems and consistency
  16. 16.Twelve-factor advises executing the app as stateless processes. Which capability does that primarily enable?
    Queues, async and stateless services
  17. 17.What is the main risk of putting a queue between a web tier and a worker without also bounding it?
    Queues, async and stateless services
  18. 18.Twelve-factor says to treat backing services as attached resources. What does that buy you?
    Queues, async and stateless services
18 questions left to answer.
Apparatus

Sources

Hiring loops change. Every claim above carries a retrieval date so you can judge how current it is.

  1. [1]RFC 9110, HTTP Semantics, Section 9: Methods · retrieved 2026-08-13
  2. [2]The Twelve-Factor App · retrieved 2026-08-13
  3. [3]PostgreSQL Documentation, Transaction Isolation · retrieved 2026-08-13
Keep preparing

Refresh your memory

Free learning paths covering the ground this loop tests, whatever your score. Each one ends with a shareable certificate.