Skip to content

Top 35 Backend Developer Interview Questions and Answers [2026]

October 7, 2026 7 min read
Backend Developer interview guide by Second Talent
TL;DR: Backend interviews in 2026 test HTTP semantics, database transactions and indexes, caching, queues and failure handling, whatever the language. Strong candidates know which calls are safe to retry, what their database's isolation level allows, and that HTTP gained a standard QUERY method in June 2026.

In June 2026 the IETF published RFC 10008, giving HTTP a QUERY method: a request with a body that is safe and idempotent, so clients and caches may repeat it.

It is a small change that tests a large idea, because a backend engineer who cannot say which requests are safe to repeat will also get retries, caching and queue consumers wrong.

The questions below work in any language and are grouped the way a backend loop usually runs.

Key takeaways
  1. 1PostgreSQL runs at read committed by default, which still allows non-repeatable reads, phantoms and lost updates unless the code guards against them.
  2. 2RFC 9745 (March 2025) standardized a Deprecation response header, so API retirement can be announced in the protocol, not only in release notes.
  3. 3PostgreSQL 18 ships a built-in uuidv7() function, making time-ordered UUID keys a one-line default.
  4. 4Kubernetes gives a terminating Pod 30 seconds by default to shut down gracefully before it is killed.

APIs and HTTP

1. Which HTTP methods are safe and which are idempotent, and why does it matter?

Safe methods do not change server state: GET, HEAD, OPTIONS and TRACE, plus QUERY since RFC 10008. Idempotent methods have the same effect whether sent once or ten times: all the safe methods plus PUT and DELETE.

POST and PATCH are neither by default.

It matters because clients, proxies and load balancers act on these rules. RFC 9110 section 9.2.2 lets a client automatically retry an idempotent request after a dropped connection.

A GET that deletes something, or a PUT that increments a counter, breaks that contract and turns a network blip into duplicate side effects.

2. How do you make a POST endpoint safe to retry?

Accept an idempotency key from the client and store the result under it. The client generates a unique key per logical operation, such as "create this payment", and sends it in a header. The server:

  1. Looks up the key. If a completed result exists, returns it without running the operation again.
  2. If not, records the key as in progress (a unique constraint makes concurrent duplicates fail), runs the operation and stores the response in the same transaction as the side effect where possible.
  3. Rejects a reused key with different parameters.

Stripe's API is the reference most candidates know: keys up to 255 characters, compared against the original parameters, and eligible for removal after at least 24 hours.

An IETF draft for an Idempotency-Key header exists but has not become an RFC, so the header name is a convention, not a standard.

3. How do you stop two clients overwriting each other's changes through an API?

Use optimistic concurrency with HTTP conditional requests. The GET response carries an ETag for the resource's current version.

The client sends it back in If-Match on the PUT or PATCH, and the server applies the change only if the version still matches; otherwise it returns 412 Precondition Failed and the client re-reads.

PATCH /orders/42
If-Match: "v7"
Content-Type: application/merge-patch+json

{"shipping_address": "..."}

HTTP/1.1 412 Precondition Failed

Inside the service the same idea becomes a version column: UPDATE ... SET ..., version = version + 1 WHERE id = 42 AND version = 7, with zero rows updated meaning a conflict.

The difference between PUT and PATCH is worth hearing too: PUT replaces the whole representation, PATCH applies a described change.

4. Which status code: 400, 409, 422, 429 or 503?

  • 400 Bad Request: the request is malformed, such as invalid JSON.
  • 422 Unprocessable Content: the syntax is fine but the content fails validation, such as an end date before the start date.
  • 409 Conflict: the request conflicts with the resource's current state, such as creating a user whose email already exists.
  • 429 Too Many Requests: the client hit a rate limit; include Retry-After.
  • 503 Service Unavailable: the server is overloaded or in maintenance; also send Retry-After when you know it.

The body matters as much as the code. RFC 9457 Problem Details gives errors a standard JSON shape with type, title, status and detail, so clients can branch on a stable type URI instead of parsing messages.

5. REST, gRPC or GraphQL: how do you choose?

Choose by who calls the API and how:

  • REST over JSON for public and partner APIs: every client can call it, HTTP caching works, and tooling such as OpenAPI is mature.
  • gRPC for internal service-to-service calls where a typed contract, streaming and compact Protobuf payloads matter. Browsers need a proxy or gRPC-Web.
  • GraphQL when many client screens need different shapes of the same data. It moves query cost control to the server, so it needs depth limits, cost analysis and persisted queries.

A strong candidate adds that many systems use two of them: gRPC inside, REST or GraphQL at the edge. See the GraphQL interview guide for that side.

6. How do you design an endpoint for work that takes minutes, such as a large export?

Do not hold the request open. Accept the job, return 202 Accepted with a Location header pointing to a status resource, and run the work in a background worker.

POST /exports            -> 202 Accepted, Location: /exports/881
GET  /exports/881        -> {"status": "running", "progress": 0.4}
GET  /exports/881        -> {"status": "done", "download_url": "..."}

Clients poll the status URL or receive a webhook when it finishes. The job itself should be idempotent and resumable, because workers restart.

Long synchronous requests fail in ways unrelated to your code: load balancer idle timeouts, client timeouts and deploys that drain connections.

Databases

7. How does a B-tree index speed up a query, and when does it not help?

A B-tree keeps keys sorted, so the database finds a value or a range in a few page reads instead of scanning the table.

A composite index on (customer_id, created_at) serves WHERE customer_id = ? and WHERE customer_id = ? AND created_at > ?, plus ORDER BY created_at for one customer.

It does not help when:

  • The query skips the leading column, such as filtering only by created_at.
  • The column is wrapped in a function, such as WHERE lower(email) = ?, unless there is an expression index.
  • The filter matches a large share of rows, where a sequential scan is cheaper.
  • Types do not match and force a cast on the column.

Every index also slows writes and takes space, so candidates should talk about removing unused ones too.

8. What is the N+1 query problem, and how do you catch it?

Code loads a list with one query, then runs one more query per item to load a related record: 1 query for 50 orders, then 50 for their customers. Each query is fast, so it passes review and hurts only as data grows.

The fix is to load related data in bulk: a join, or a second query with WHERE id IN (...). Every ORM has a way to say this (eager loading, include, select_related).

To catch it before production, assert query counts in tests for list endpoints and watch traces, where N+1 shows up as a ladder of identical database spans.

9. What do the transaction isolation levels prevent?

Each level rules out more of the anomalies concurrent transactions can cause: dirty reads, non-repeatable reads, phantom reads and serialization anomalies. The SQL standard defines the minimum for each level; databases may prevent more.

Grid of PostgreSQL isolation levels against anomalies: read uncommitted and read committed allow non-repeatable reads, phantoms and serialization anomalies; repeatable read allows only serialization anomalies; serializable prevents all four.

In PostgreSQL, read uncommitted behaves like read committed, and repeatable read also blocks phantoms, which the standard allows.

Only serializable prevents every anomaly, and it does so by aborting some transactions with a serialization failure, so the application must be ready to retry the whole transaction.

Defaults differ. MySQL's InnoDB defaults to repeatable read, PostgreSQL to read committed. Code moved between the two can change behavior without raising any error.

10. Optimistic or pessimistic locking?

Pessimistic locking takes the lock up front with SELECT ... FOR UPDATE: other writers wait. Optimistic locking lets everyone proceed and detects conflicts at write time with a version column, as in question 3.

Pessimistic
  • SELECT ... FOR UPDATE before the change
  • Other writers wait for the lock
  • Fits hot rows with frequent conflicts
Optimistic
  • version column checked in the UPDATE
  • Losers get a conflict and retry
  • Fits rare conflicts and retryable edits

Use pessimistic locks when conflicts are frequent and retrying is expensive, such as decrementing stock on a hot product, and keep the transaction short.

Use optimistic locks when conflicts are rare and the operation can be retried, such as editing a profile.

A candidate should also know the third option for simple counters: an atomic UPDATE ... SET stock = stock - 1 WHERE stock > 0 needs no read at all.

11. What breaks when you add a read replica?

Read-your-writes. Replication is asynchronous in most setups, so a user who saves a change and is then served from a replica may see the old value. The lag is usually milliseconds and occasionally much longer.

Ways to handle it: send a user's reads to the primary for a short window after they write, route by consistency need (the checkout reads from the primary, the product catalog from replicas), or pass a replication position and wait until the replica has caught up.

Monitoring replication lag and pulling a lagging replica out of rotation belongs in the answer too.

12. SQL or NoSQL: how do you choose?

Start from access patterns and consistency needs, not from the label. A relational database fits data with relationships, ad hoc queries and multi-row transactions, which covers most business systems.

A document store fits aggregates read and written as one unit. A wide-column or key-value store fits huge write volumes with queries known in advance, such as time series keyed by device.

Listen for outdated generalizations.

"NoSQL is eventually consistent" is wrong as a blanket statement: MongoDB reads from the primary by default and supports multi-document transactions, and DynamoDB offers strongly consistent reads as an option.

The better answer names the specific guarantee each option gives for the queries the product needs.

Caching and Queues

13. Compare cache-aside, write-through and write-behind caching.

  • Cache-aside: the application reads the cache, and on a miss reads the database and fills the cache. Writes go to the database and delete the cache key. The most common pattern, and the cache can fail without losing data.
  • Write-through: every write goes to the cache and the database together, so the cache stays warm. Writes get slower, and rarely read data fills the cache.
  • Write-behind: writes go to the cache and are flushed to the database later. Fast writes, but a cache failure loses data. Rare outside specialized systems.

14. How do you keep a cache from serving stale data?

Combine a TTL with invalidation on write, and know the race you are accepting. Deleting the key after updating the database is the usual choice.

Setting the new value instead can go wrong when two writers race and the older value lands last.

Even delete-on-write has a gap: a reader can miss, read the old row, then write it to the cache just after the writer's delete. A TTL bounds how long that stale value lives.

For data that must never be stale, such as permissions or balances, the honest answer is not to cache it, or to use versioned keys that change when the data changes.

15. How do HTTP caching headers work for an API?

Cache-Control tells browsers and CDNs whether and how long to cache: max-age=60, private for per-user data, no-store for anything sensitive. ETag plus If-None-Match lets a client revalidate and get 304 Not Modified with no body.

The classic bug is a shared cache storing a personalized response. Responses that depend on the Authorization header or a cookie need private or no-store, and responses that vary by a request header need Vary. RFC 9111 defines the rules.

16. A worker crashes halfway through a job. What happens to the job?

With most queues, it runs again. SQS makes the message visible again after its visibility timeout; RabbitMQ requeues unacknowledged messages when the consumer's channel closes; Kafka redelivers from the last committed offset.

That is at-least-once delivery, and it means every job must be safe to run twice.

Three things follow. Acknowledge only after the work is done. Make the work idempotent, for example with a processed-message table keyed by message ID, or with upserts.

And cap the attempts: a message that fails every time (a "poison" message) should move to a dead letter queue after a few tries instead of blocking or looping. The event-driven architecture guide goes deeper on delivery guarantees.

17. How do you run a scheduled job exactly once when the service runs on ten instances?

Do not rely on each instance's cron. Options, from simplest:

  • Run the schedule in one place, such as a Kubernetes CronJob with concurrencyPolicy: Forbid, which enqueues work for the service to process.
  • Take a lock before running: a PostgreSQL advisory lock or a Redis lock with an expiry, so only the instance that wins runs the job.
  • Enqueue with a unique job key per period, such as daily-report:2026-09-28, and let the queue drop duplicates.

A lock with an expiry can still let two instances run if the first stalls past the expiry, so the job itself should remain idempotent.

Reliability

18. How do you set timeouts across a chain of service calls?

Every network call gets a timeout, and the timeouts shrink as you go down the chain. If the edge gives a request 2 seconds, a service two hops down cannot be allowed 5.

Pass a deadline with the request (gRPC does this natively) and have each hop use what remains.

Pick values from measured latency, typically a little above the dependency's p99, not a round guess.

A missing timeout is the most common cause of a slow dependency turning into a full outage: threads or connections wait forever, the pool empties and healthy requests queue behind them.

19. When should a failed call be retried, and how?

Retry only transient failures, only idempotent operations, and only with backoff and jitter.

Decision flow for retrying a failed call: fail fast on non-transient errors, do not retry non-idempotent operations, give up when attempts or the deadline run out, otherwise wait with backoff and jitter.

Exponential backoff spreads retries out; jitter stops thousands of clients retrying in lockstep.

The AWS Builders' Library article on the topic explains both, and adds a point candidates often miss: retries at every layer multiply, so a call three layers deep with three attempts each can hit the bottom service 27 times.

Retry at one layer.

async function withRetry(fn, { attempts = 4, baseMs = 100, capMs = 2000 } = {}) {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (err) {
      if (!err.retryable || i === attempts - 1) throw err;
      const backoff = Math.min(capMs, baseMs * 2 ** i);
      await sleep(Math.random() * backoff);            // "full jitter"
    }
  }
}

20. How do you stop one slow dependency from taking down the whole service?

Isolate it and fail fast:

  • Timeouts so no call waits forever.
  • Bulkheads: a separate connection pool or concurrency limit per dependency, so a slow recommendations API cannot use every thread the checkout path needs.
  • Circuit breaker: after enough failures, stop calling the dependency for a cool-down period and return a fallback, then let a few trial calls through.
  • Load shedding: when the service itself is saturated, reject excess requests early with 503 rather than accepting work it cannot finish.

The fallback is a product decision: an empty recommendations widget is fine, a missing price is not.

21. What should liveness and readiness checks test?

According to the Kubernetes docs, liveness probes decide when to restart a container, and readiness probes decide when it may receive traffic.

So liveness should answer only "is this process stuck?", and readiness "can this instance serve right now?".

The common mistake is checking the database in the liveness probe. When the database has a blip, every instance fails liveness at once and Kubernetes restarts them all, turning a short outage into a long one.

Dependency checks, if any, belong in readiness, and even there with care.

22. How do you deploy without dropping in-flight requests?

Handle SIGTERM properly. When Kubernetes terminates a Pod it sends SIGTERM and waits for the grace period, which defaults to 30 seconds, before killing the process. On SIGTERM the service should:

  1. Fail its readiness check so no new traffic arrives. Load balancers take a few seconds to notice, so keep serving briefly.
  2. Stop accepting new connections and let in-flight requests finish.
  3. Stop pulling from queues and finish or release the current message.
  4. Close database pools and exit before the grace period ends.

Many runtimes exit immediately on SIGTERM by default, which is how deploys produce a small spike of 502s that nobody can reproduce.

23. p99 latency doubled but the average did not move. How do you investigate?

Look at the slow requests themselves, not aggregates. The average hides a tail: a small share of requests got much slower. Split p99 by endpoint, instance and dependency to see whether it is one route, one host or one downstream call.

Then open traces for requests in the slow tail, where a metric exemplar or a trace search by duration takes you straight to one.

Typical findings: lock waits in the database, a cold cache after a deploy, garbage collection pauses on one instance, connection pool exhaustion, or a noisy neighbor on shared hardware.

The observability interview guide covers tracing and SLOs in depth.

Security

24. How should a backend store passwords?

With a slow, salted password hash, never a fast hash like SHA-256 and never encryption.

The OWASP Password Storage Cheat Sheet recommends Argon2id with at least 19 MiB of memory, 2 iterations and 1 degree of parallelism, then scrypt, then bcrypt with a work factor of 10 or more, and PBKDF2-HMAC-SHA256 at 600,000 iterations where FIPS compliance requires it.

Two details separate strong answers: bcrypt reads at most 72 bytes of input, and hashes should be upgraded on the user's next successful login when parameters change.

25. "We use an ORM, so SQL injection is solved." Is it?

Mostly, but not entirely. Parameterized queries, which ORMs use by default, keep user input out of the SQL text.

Injection comes back through the escape hatches: raw SQL built with string formatting, dynamic ORDER BY or column names, and search features that assemble query fragments.

Identifiers such as table and column names cannot be bound as parameters, so the OWASP SQL Injection Prevention Cheat Sheet recommends mapping user input to an allowlist of known values:

const SORTS = { newest: "created_at DESC", price: "price ASC" };
const orderBy = SORTS[req.query.sort] ?? SORTS.newest;   // never the raw input
const rows = await db.query(
  `SELECT id, name FROM products WHERE category = $1 ORDER BY ${orderBy}`,
  [req.query.category]
);

26. A database password leaked. How do you rotate it without downtime?

Use two valid credentials during the switch. Create a second user or password, deploy the service with the new one, confirm that no connections still use the old one, then revoke it.

Doing it in the reverse order, revoking first, takes the service down until the deploy finishes.

Services should read secrets from a secret manager at startup or on a refresh interval, not from the image, so rotation needs no code change.

Asking how long the last rotation took is a good follow-up: a team that has never rotated a secret usually cannot do it quickly under pressure.

Hiring Test Exercises

27. Design a URL shortener. What should a strong answer cover?

It is the most common backend hiring test because it is small enough for an hour and still has real trade-offs. Listen for:

  • Key generation: a counter encoded in base62, or random keys with a collision check, and why sequential keys are guessable.
  • Read path: redirects outnumber writes by orders of magnitude, so the lookup is cached and the choice between 301 and 302 affects analytics.
  • Storage: a single key-value lookup, which any database handles; sharding only when the numbers justify it.
  • Abuse: rate limits on creation and checks for malicious target URLs.

Weak answers jump to Kafka and ten microservices before estimating the traffic.

28. Design webhook delivery for your product. What matters?

Reliability and trust. The sender should write each event to its own table in the same transaction as the change that caused it, then deliver from there, so no event is lost if the process dies.

Delivery retries with backoff for hours or days, and a failing endpoint is paused and its owner notified.

Receivers need to trust the messages: sign each payload with an HMAC over the body and a timestamp, and reject old timestamps to block replays. Events carry an ID so receivers can deduplicate, because delivery is at-least-once.

Ordering is not guaranteed, so each event should carry enough state or a version for the receiver to ignore stale ones.

29. Send a one-time email to 2 million users. How do you build it?

As a resumable batch, not a loop in a request. Page through users by primary key (not OFFSET), enqueue jobs in chunks, and record each send in a table with a unique constraint on (campaign, user) so a restart never emails anyone twice.

Throttle to the email provider's rate limit, handle its 429 responses with backoff, respect unsubscribes checked at send time rather than at enqueue time, and add a kill switch.

A candidate who asks "what happens if we must stop halfway?" before writing code is showing the right instinct.

30. Review this handler. What is wrong with it?

Code review exercises show more than algorithm puzzles. A handler with several planted bugs:

app.post("/transfer", async (req, res) => {
  const { from, to, amount } = req.body;
  const src = await db.one("SELECT balance FROM accounts WHERE id = $1", [from]);
  if (src.balance < amount) return res.status(400).send("insufficient funds");
  await db.none("UPDATE accounts SET balance = balance - $1 WHERE id = $2", [amount, from]);
  await db.none("UPDATE accounts SET balance = balance + $1 WHERE id = $2", [amount, to]);
  res.send("ok");
});

A strong reviewer finds: no check that the caller owns the from account; no validation that amount is a positive number; the two updates are not in a transaction, so a crash between them loses money; the balance check and the debit race, so two concurrent transfers can overdraw (fix with SELECT ... FOR UPDATE or a conditional update); and no idempotency key, so a client retry sends the money twice.

What Changed Recently

Mar 2024
Redis leaves BSD; the Linux Foundation forms Valkey
May 2024
RFC 9562 defines UUID version 7
Mar 2025
RFC 9745: the Deprecation response header
Sep 25, 2025
PostgreSQL 18 with asynchronous I/O and uuidv7()
May 21, 2026
OpenTelemetry graduates at the CNCF
Jun 2026
RFC 10008: the HTTP QUERY method

31. What is the HTTP QUERY method?

RFC 10008 (June 2026) defines QUERY: a request whose body describes a query, which the server processes "in a safe and idempotent manner".

It fills the gap between GET, which has no defined body semantics and runs into URL length limits, and POST, which clients and caches must assume changes state.

QUERY /orders
Content-Type: application/json
Accept: application/json

{"status": ["paid", "shipped"], "created_after": "2026-09-01", "limit": 50}

Because QUERY is safe and idempotent, clients may retry it automatically and the response is cacheable, with the body forming part of the cache key. Servers advertise which query formats they accept in the new Accept-Query response header.

Support in frameworks, proxies and CDNs will take time, so the practical answer in 2026 is "use it where the whole path supports it, and keep a POST fallback".

32. How should an API announce that an endpoint is deprecated?

In the response, as well as in the changelog.

RFC 9745 (March 2025) defines a Deprecation header carrying the date the resource was or will be deprecated, and pairs it with the older Sunset header from RFC 8594 for the date it stops working:

Deprecation: @1688169599
Sunset: Sun, 30 Jun 2024 23:59:59 UTC
Link: <https://api.example.com/docs/v2-migration>; rel="deprecation"; type="text/html"

The @ value is a Unix timestamp in the structured-field date format. Clients and API gateways can log or alert on the header automatically.

Pair it with usage metrics per client so you know who still calls the old version before the sunset date.

33. Should new tables use UUIDv7 primary keys?

For many systems, yes. RFC 9562 (May 2024) defines UUID version 7, whose first 48 bits are a Unix timestamp in milliseconds.

New IDs therefore arrive in roughly increasing order, so B-tree inserts land at the end of the index instead of at random pages, which is the main cost of random UUIDv4 keys.

PostgreSQL 18, released on September 25, 2025, generates them natively with uuidv7().

The trade-offs a strong candidate names: 16 bytes versus 8 for a bigint, and the creation time is readable from the ID, which can leak information if IDs are public.

34. Redis changed its license. What does that mean for a backend team?

It changed twice. In March 2024 Redis moved from the BSD license to a dual RSALv2 and SSPLv1 license, and the Linux Foundation announced Valkey on March 28, 2024, a fork continuing from Redis 7.2.4 under the BSD license.

With Redis 8.0 in May 2025, Redis added AGPLv3 as a third license option.

For most teams using Redis as a cache over the network, none of the licenses restrict use.

The questions that matter are which engine your cloud provider's managed service now runs, whether you depend on features present in only one of them, and whether your legal team accepts AGPL.

Valkey kept the Redis protocol, so most client libraries connect to either.

35. Why has OpenTelemetry become the default for backend telemetry?

Because it is vendor-neutral and now mature.

The CNCF graduated OpenTelemetry on May 21, 2026, and its semantic conventions for database client spans have been stable for PostgreSQL, MySQL, MariaDB and SQL Server since release 1.33.0 in May 2025.

For a backend engineer this means instrumenting once with the OpenTelemetry API and choosing or switching backends through configuration, with attribute names such as db.system.name that every tool understands.

Expect to be asked how a trace ID reaches your logs and how you would add a custom span around a slow block of code.

Signs of a Strong Answer

  • They ask about traffic, data size and consistency needs before choosing technology.
  • They know their own database's default isolation level and what it allows.
  • They treat every network call as able to fail, time out or succeed twice, and design for each.
  • They name the race in a cache or balance update without being prompted.
  • They talk about graceful shutdown, timeouts and retries as numbers they have set, not concepts.
  • In the code review exercise, they find the missing authorization check, not only the missing transaction.

Hiring Backend Developers

Backend skill shows in how a candidate handles failure, and a live exercise reveals that faster than a résumé. Second Talent matches companies with pre-vetted back-end developers from Asia, screened with questions and exercises like these.

See our backend developer cost guide for current rates.

Tell us the stack and we send a shortlist within 24 hours. Start hiring, or see our PostgreSQL and microservices interview guides.

Ready to hire? Hire Back End Developers: vetted AI-native talent, with first profiles in 24 hours.

Get developers who ship with AI agents.

Starts at $23/Hour.

Start Hiring

$0 cost until you hire