Backend Development Interview Questions 2026

A lot of backend interview prep gets funneled entirely into language specifics, Node's event loop, Spring's dependency injection, Django's ORM, and that's genuinely useful, but it skips the layer of knowledge that transfers across every single one of those stacks: how you actually design an API that won't need a breaking rewrite in a year, why your database queries are quietly doing 50 round trips instead of 1, and what happens the moment a downstream service you depend on starts timing out.

This page covers backend development as a discipline, independent of any specific language or framework, API design, authentication, database access patterns, caching, background processing, security, and the observability practices that separate a service you can actually operate in production from one that just works on your laptop. If you want language-specific depth, the Node.js, Spring Boot, and C# and .NET pages cover that ground directly.

Why Backend Interviews Test the Concepts Behind the Framework

Frameworks change, a team using Express today might be on Fastify or NestJS in three years, but the underlying problems every backend has to solve don't change nearly as fast. Interviewers ask framework-agnostic backend questions to check:

  • Whether you understand API design well enough to build something other teams can actually integrate with reliably
  • Whether you know why a database access pattern is slow, not just that it happens to be slow
  • Whether you've thought about what happens when something fails, a timeout, a partial failure, a downstream outage, not just the happy path
  • Whether you understand what it takes to actually operate a service in production, not just get it running once

Who This Page Is For

  • Backend and full stack developers interviewing for roles that aren't tied to one specific framework
  • Developers strong in one backend stack who want to make sure their fundamentals transfer to a new one
  • Anyone prepping for a generalist backend round that sits alongside a separate, language-specific technical round

Backend Development Interview Questions and Answers (2026)

Backend Development Fundamentals

1. What does a backend developer actually do, day to day, beyond "write server code"?

Beyond writing the actual server-side logic, a backend developer designs APIs other teams (frontend, mobile, other services) will depend on, models and manages data in a database, handles authentication and authorization, integrates with third-party services, and increasingly, thinks about how a service actually behaves in production, its performance under load, how it fails, how it's monitored. A meaningful share of real backend work is genuinely about these surrounding concerns, not just the core business logic itself.

2. What's the difference between the client-server model and a peer-to-peer model, and why does virtually every backend service you'll build fit into the former?

In a client-server model, clients (browsers, mobile apps, other services) send requests to a centralized server that processes them and sends back responses, a clear, asymmetric relationship. In a peer-to-peer model, participants communicate directly with each other as equals, with no single central authority. Almost every backend service you'll build as a professional developer is client-server, a REST API, a web app's backend, a mobile app's server, because centralizing logic, data, and authority in one place is dramatically simpler to build, secure, and reason about than coordinating a fully decentralized system, peer-to-peer architectures are powerful but genuinely niche outside specific domains like blockchain or certain file-sharing protocols.

3. What's the difference between a stateless and a stateful backend service, and why do most modern backends lean stateless wherever possible?

A stateless service doesn't retain any client-specific data between requests, each request carries everything the server needs to process it (often via a token), and any server instance can handle any request. A stateful service retains data about a client's session in server memory, which means subsequent requests from that client need to reach the same server instance. Most modern backends lean stateless specifically because it makes horizontal scaling trivial, any instance behind a load balancer can serve any request, and it makes recovering from an instance failure simple, no session data is lost since there wasn't any tied to that specific instance.

4. Walk through what actually happens between a client sending an HTTP request and your backend sending back a response.

The request travels over the network (often through a load balancer, and potentially a CDN or API gateway first) to your server, where the framework's routing layer matches the URL and method to the correct handler. Middleware typically runs first, parsing the request body, checking authentication, logging. Your handler then executes, usually querying a database or calling another service, applies business logic to that data, and constructs a response, which gets serialized (commonly to JSON), given the appropriate status code and headers, and sent back over the same connection to the client.

5. What's the difference between a monolith and microservices from the day-to-day developer's perspective, not just the architecture diagram?

In a monolith, your entire application lives in one codebase and deploys as one unit, calling another part of the system is usually just a direct function call, fast, and you get compile-time (or at least single-process) guarantees about whether it exists and what it returns. In microservices, calling "another part of the system" means a network call to a separate service, which can fail, time out, or return unexpected data in ways a local function call never would, and you're now also managing separate deployments, separate logging, and version compatibility between services. Day to day, this means microservices genuinely trade simplicity for independent scalability and deployability, a tradeoff that's only worth it once a monolith's size or team coordination overhead becomes the actual bottleneck.

6. What is a 12-factor app, and why do so many backend teams still reference it even though it's over a decade old?

The Twelve-Factor App is a set of principles for building backend services that are portable, scalable, and easy to operate in modern, cloud-based environments, things like storing configuration in environment variables rather than hardcoded files, treating backing services (databases, caches) as attachable resources rather than tightly coupled dependencies, and keeping the app stateless so any instance can handle any request. It's still referenced constantly because the underlying problems it addresses, config management, statelessness, clean separation between build and run stages, are exactly as relevant to a team running containers on Kubernetes today as they were to teams deploying to early cloud platforms when it was written.


RESTful API Design

1. What makes an API actually "RESTful," beyond just using JSON over HTTP?

REST is an architectural style built around a specific set of constraints, a stateless client-server relationship, a uniform interface (consistent use of HTTP methods and resource-based URLs), resources that can be identified and manipulated through representations (typically JSON), and, more strictly, hypermedia links that let clients discover related actions (a constraint most real-world "RESTful" APIs actually skip in practice). Just returning JSON over HTTP doesn't automatically make an API RESTful, plenty of APIs use JSON and HTTP but violate REST's core constraints, using a single POST endpoint for every operation regardless of its actual semantics, for example, is a common pattern that isn't genuinely RESTful despite superficially looking similar.

2. How do you decide what should be a resource (a noun in your URL) versus an action, and why do a lot of APIs end up with awkward, verb-like endpoints?

Resources should represent the actual nouns in your domain, users, orders, invoices, and the HTTP method (GET, POST, PUT, DELETE) expresses the action being taken on that resource, rather than the action being baked into the URL itself, /orders/123 with a DELETE request, not /deleteOrder/123. APIs end up with awkward, verb-like endpoints (/activateUser, /sendInvoice) when an operation doesn't map cleanly onto a simple CRUD action on a single resource, a common, pragmatic fix is modeling the action itself as a sub-resource, POST /users/123/activations or POST /invoices/456/send, keeping the URL noun-based while still expressing a specific action.

3. What's the difference between PUT and PATCH? People use them interchangeably but they mean genuinely different things.

PUT is meant to replace an entire resource with the payload you send, if you omit a field, the semantically correct behavior is for that field to be cleared or reset to its default, since you're replacing the whole thing. PATCH is meant for partial updates, you send only the fields you want to change, and everything else on the resource stays untouched. Using PUT when you actually mean PATCH is a common, subtle bug source, a client updating just one field via PUT, expecting other fields to be left alone, can accidentally wipe out data it never intended to touch.

4. What's the actual difference between a 401 and a 403 status code, and why does that distinction matter for how a client should react?

401 (Unauthorized) means the request lacks valid authentication entirely, the server doesn't know who you are, the correct client response is typically to prompt for login or refresh credentials. 403 (Forbidden) means the server does know who you are, you're authenticated, but you don't have permission to perform this specific action, re-authenticating won't fix a 403, the client needs to communicate that the user simply isn't allowed to do this, not prompt them to log in again. Returning the wrong one of these two codes is a genuinely common API mistake that leads to confusing client-side error handling.

5. How would you version an API, and what are the tradeoffs between URL versioning, header versioning, and just not versioning at all?

URL versioning (/v1/users, /v2/users) is explicit and easy for consumers to understand and test directly in a browser or with curl, but it leads to duplicated routes over time and can encourage sprawling, long-lived old versions. Header versioning (a custom header or the Accept header specifying a version) keeps URLs clean and stable, but is less discoverable and easy to forget to set correctly. Not versioning at all works only if you're strictly disciplined about never making breaking changes, only ever adding new, optional fields, which is genuinely achievable for a while but tends to break down eventually as requirements evolve in ways that don't fit cleanly into additive-only changes.

6. How would you design pagination for an endpoint that returns a large list of results? Offset-based versus cursor-based, and why does cursor-based generally scale better?

Offset-based pagination (?page=3&limit=20) is simple to implement and understand, but it degrades in both performance and correctness at scale, the database still has to scan and discard all the skipped rows to reach a high offset, and if rows are inserted or deleted between page requests, results can shift, causing a client to see duplicates or miss items entirely. Cursor-based pagination (?after=<opaque_cursor>) uses a stable reference point, often based on a unique, sortable field like an ID or timestamp, letting the database jump directly to the right starting point without scanning past rows, and it stays consistent even if data changes between requests, since each page's boundary is anchored to a specific record, not a shifting numeric offset.

7. What does it mean for an API endpoint to be idempotent, and why does that matter specifically for retry logic?

An idempotent endpoint produces the same result and end state no matter how many times the identical request is sent, calling it once or five times leaves the system in the same state. GET, PUT, and DELETE are conventionally idempotent, POST conventionally is not. This matters enormously for retries, if a client doesn't receive a response (maybe the network dropped the response even though the server actually processed the request), it needs to safely retry without risking a duplicate side effect, charging a payment twice, creating two identical orders, which is exactly why non-idempotent operations like payment creation often use an explicit idempotency key the client generates, letting the server recognize and safely ignore a retried request instead of double-processing it.


Authentication and Authorization

1. What's the actual difference between authentication and authorization from an implementation perspective, not just the dictionary definition?

Authentication is implemented as verifying a credential, a password, a token signature, and establishing who the requester actually is, typically resulting in some identity being attached to the request context (a user ID, a set of claims). Authorization is implemented as a separate check, given that established identity, does this specific request have permission to do what it's asking to do, usually a lookup against roles or permissions, evaluated per-endpoint or per-resource. Conflating the two in code, treating "the token is valid" as equivalent to "this user is allowed to do this" is a genuinely common and dangerous mistake, valid authentication says nothing on its own about what a user is actually permitted to do.

2. How would you implement session-based authentication in a backend service, and what has to happen on both login and every subsequent request?

On login, after verifying credentials, the server creates a session record (in memory, a database, or a store like Redis) containing the user's identity, and sends the client a session ID, typically in a cookie. On every subsequent request, the server looks up that session ID against the session store to retrieve the associated user and confirm the session hasn't expired or been revoked, before proceeding with the request. This requires the session store to be accessible from every server instance handling requests, if you're running multiple backend instances behind a load balancer, session data needs to live in a shared store, not just in one instance's local memory, or a user's requests hitting a different instance would fail to find their session.

3. What's the difference between a JWT's payload being encoded versus encrypted, and why does that distinction matter for what you should and shouldn't put inside one?

A standard JWT's payload is Base64-encoded and signed, not encrypted, the signature guarantees the payload hasn't been tampered with, but anyone who intercepts the token can trivially decode and read its contents, encoding is not a form of confidentiality. This matters directly for what you put inside a JWT, non-sensitive claims (a user ID, a role, an expiration time) are fine, but genuinely sensitive data (a password, a full credit card number, private personal information) should never go inside a standard JWT payload, since it's readable by anyone holding the token, if you need actual confidentiality, you'd need an encrypted JWT (JWE) specifically, not the standard signed JWT (JWS) most APIs use.

4. How would you handle token expiration and refresh in a backend API without forcing the user to log in again constantly?

A common pattern uses two tokens, a short-lived access token (minutes to an hour) used for actual API requests, limiting the damage if it's ever compromised, and a longer-lived refresh token, stored more securely, used specifically to obtain a new access token once the current one expires, without requiring the user to re-enter credentials. The refresh token itself should be revocable (checked against a store server-side) so a compromised session can actually be terminated, unlike a pure, stateless access token which remains valid until its own expiration no matter what the server wants to do about it in the meantime.

5. How would you implement role-based access control (RBAC) at the API level? Where does the actual permission check belong?

Each authenticated user is assigned one or more roles (admin, editor, viewer), and each role maps to a defined set of permissions. The actual permission check typically belongs in a middleware or guard layer that runs after authentication but before the actual handler logic executes, checking whether the authenticated user's role includes the permission required for this specific endpoint and action, rejecting the request with a 403 immediately if not. Keeping this check centralized in middleware, rather than scattered as ad hoc if-checks inside individual handler functions, makes permissions far easier to audit and keep consistent across a growing API surface.


Database Access Patterns

1. What's the actual tradeoff between using an ORM and writing raw SQL, beyond "ORMs are slower"?

An ORM lets you work with your database through your programming language's native objects and methods, which speeds up development, reduces boilerplate, and often adds a layer of protection against SQL injection by default. The real tradeoff isn't primarily raw speed, a well-used ORM's overhead is often negligible, it's that an ORM can obscure exactly what SQL is actually being generated and executed, which becomes a real problem the moment you need to understand or optimize a specific slow query, or when the ORM's generated query genuinely isn't the most efficient way to express what you actually need, which is exactly why most serious backend systems use an ORM for the common cases and drop down to raw SQL or a query builder for the specific queries that need tighter control.

2. What is connection pooling, and what actually goes wrong if your backend opens a new database connection for every single request?

A connection pool maintains a set of already-established, reusable database connections that requests borrow and return, rather than opening and tearing down a brand new connection for every request. Opening a fresh connection per request is expensive, the TCP handshake and database authentication both add real, measurable latency to every single request, and under real traffic, you can quickly exhaust the database's maximum connection limit, since connections are being created faster than the (also non-trivial) teardown process can release them, connection pooling avoids both problems by reusing a bounded, already-warmed set of connections instead.

3. What is a database transaction, and how would you actually decide what should and shouldn't be wrapped in one?

A transaction groups multiple database operations into a single, atomic unit, either all of them succeed and get committed together, or if any fails, all of them roll back, leaving the database as if none had happened. You'd wrap operations in a transaction when they're logically dependent on each other completing together, transferring money between two accounts (debit one, credit the other) absolutely needs to be atomic, a partial failure leaving one side updated and the other not would corrupt your data. You generally wouldn't wrap unrelated, independent operations in the same transaction unnecessarily, since transactions hold locks and resources for their duration, and an overly broad transaction can hurt concurrency and performance for no real correctness benefit.

4. How would you handle the N+1 query problem in a backend API that returns a list of items with related data?

The N+1 problem happens when you fetch a list of N items with one query, then loop over that list and issue a separate query for each item's related data, N additional queries instead of one combined query, for example fetching 50 blog posts, then querying each post's author individually inside a loop. The fix is to batch that related fetch into a single query upfront, using a SQL join, or your ORM's eager-loading feature (include, with, populate, depending on the ORM), so you end up with a small, fixed number of total queries regardless of how many items are in the list, rather than a query count that scales linearly with result size.

5. What is a database migration, and why is manually running ALTER TABLE statements against production a bad long-term practice?

A migration is a version-controlled, code-defined description of a schema change, generated or written to be applied consistently and repeatably across every environment, dev, staging, production, in the correct order. Manually running ALTER TABLE statements directly against production works in the moment, but leaves no record of what changed, when, or why, and there's no reliable, repeatable way to bring another environment (a new developer's local database, a disaster-recovery replica) up to that same schema state later, migrations solve exactly this by making schema changes reproducible, reviewable, and rollback-able like any other code change.


Error Handling and API Robustness

1. What does a consistent, well-designed API error response actually look like, and why does inconsistency here cause real pain for API consumers?

A well-designed error response uses a consistent structure across every endpoint, typically including a machine-readable error code (not just a human-readable message, so client code can programmatically branch on it), a clear, human-readable message, and the appropriate HTTP status code that matches the actual error category. Inconsistency, one endpoint returning { error: "message" }, another returning { errors: [...] }, a third just returning a raw string, forces every API consumer to write special-case handling per endpoint instead of one shared, reliable error-handling path, which is exactly the kind of friction that makes an API genuinely painful to integrate with, even if every individual endpoint technically "works."

2. What's the difference between validating input at the API boundary versus deep inside your business logic, and why do you generally want both?

Validating at the API boundary (checking request shape, required fields, types, basic format) catches malformed requests immediately, before they ever reach your actual business logic, giving fast, clear feedback and keeping your core logic free from needing to defensively re-check basic structural correctness on every call. Validating within business logic checks domain-specific rules that depend on more than just the request's shape, is this discount code actually still valid, does this user have enough balance for this transaction, rules that genuinely can't be checked from the request alone. You want both because boundary validation alone would let structurally valid but business-invalid requests through, and business-logic-only validation means malformed requests waste processing time before finally failing deep in your code.

3. What is a timeout, and why does every outbound call your backend makes need one?

A timeout is a maximum duration your code will wait for a response before giving up and treating the call as failed, rather than waiting indefinitely. Every outbound call, to a database, another internal service, a third-party API, needs one because without it, a single slow or hung downstream dependency can tie up your backend's own resources (threads, connections) waiting forever, which can cascade into your own service becoming unresponsive too, even though the actual root problem is somewhere else entirely, a missing timeout is one of the most common root causes behind an outage in one service silently taking down another.

4. What's the difference between a retry and a circuit breaker, and why does blindly retrying a failing downstream call sometimes make an outage worse?

A retry simply attempts the same failing call again, sometimes with a delay or backoff, hoping the transient issue has resolved. A circuit breaker tracks a downstream dependency's recent failure rate, and once it crosses a threshold, it "opens," causing further calls to fail immediately (or fall back to a default) without even attempting the actual request, for a cooldown period, before allowing a few test requests through to check if the dependency has recovered. Blindly retrying without a circuit breaker can make an outage worse specifically because a struggling, already-overloaded downstream service now gets hit with repeated retry traffic from every caller simultaneously, exactly when it can least handle additional load, a circuit breaker exists specifically to stop compounding an existing problem.

5. How would you design your API to handle a partial failure, where some but not all of a multi-step operation succeeded?

The right approach depends on whether the operation can genuinely be made atomic (wrapped in a database transaction, so a partial failure rolls back cleanly to a consistent state) or whether it inherently spans systems that can't share a single transaction (your database plus an external payment API, for example). For the latter case, you'd typically design for eventual consistency, tracking the operation's actual progress explicitly (a status field: pending, partially-completed, completed, failed), so a partial failure leaves a clearly identifiable, recoverable state rather than silently leaving your data in an ambiguous, unrecorded condition, and building a reconciliation or retry mechanism to complete or roll back the remaining steps.


Caching in Backend Applications

1. What's the difference between caching at the application level (in-memory or Redis) and caching at the HTTP level (using headers)?

Application-level caching stores computed results, query results, expensive computation output, directly in your backend's memory or an external cache like Redis, which your own code explicitly checks and populates. HTTP-level caching uses standard headers (Cache-Control, ETag) to let intermediaries, the client's browser, a CDN, a proxy, cache and serve responses without ever reaching your backend at all for a repeat request. Application-level caching gives you fine-grained control over exactly what's cached and for how long, HTTP-level caching can eliminate load on your backend entirely for cacheable requests, since the request never even arrives, the two are genuinely complementary, not competing approaches.

2. What is the cache-aside pattern, and what actually happens on a cache miss?

In the cache-aside pattern, your application code checks the cache first when it needs data, if the data is there (a cache hit), it's returned directly. If it's not there (a cache miss), your code queries the actual source of truth (typically the database), stores that result in the cache for next time, and then returns it to the caller. This is the most common caching pattern specifically because the cache stays a simple, transparent layer, your application logic explicitly controls what gets cached and when, rather than the cache automatically intercepting and managing data behind the scenes.

3. What is an ETag, and how does it let a client avoid re-downloading data that hasn't actually changed?

An ETag is a version identifier (often a hash of the resource's content) the server includes in a response header. On a subsequent request, the client can send that ETag back via an If-None-Match header, and if the resource hasn't changed since, the server responds with a lightweight 304 Not Modified, no response body at all, telling the client its cached copy is still valid, instead of resending the full response payload again unnecessarily.

4. What's the hardest part about cache invalidation in a real backend system, and how do you actually approach it?

The genuinely hard part is knowing precisely when cached data has become stale relative to everything else that might have changed it, especially in a system with multiple services or code paths that can all independently update the same underlying data, if any of them forgets to invalidate the relevant cache entry, you end up silently serving stale data with no obvious symptom until someone notices. Common approaches include a reasonably short TTL (time-to-live) as a safety net that bounds how stale data can ever get even if invalidation is missed somewhere, explicit invalidation triggered directly by the specific code path that writes the data, and, for more complex cases, tagging cache entries by the data they depend on so a single write can invalidate every cache entry that could possibly be affected by it.

5. What's the difference between a Cache-Control header set to "no-cache" and one set to "no-store"?

no-cache is a bit of a misleading name, it doesn't actually prevent caching, it means the cached response can be stored, but must be revalidated with the server (typically via an ETag check) before being reused, so you always get a fresh validity check even if the actual content ends up being unchanged. no-store is the genuinely strict one, it means the response must not be cached or stored anywhere at all, appropriate for responses containing sensitive data that shouldn't persist in any cache, a browser's disk cache, an intermediary proxy, even temporarily.


Asynchronous and Background Processing

1. Why would you offload work to a background job instead of just doing it synchronously within the API request?

If a piece of work is slow (sending an email, processing a large file, generating a report) or doesn't need to complete before you respond to the client, doing it synchronously ties up the request for the entire duration, forcing the client to wait unnecessarily and consuming a request-handling thread or connection the whole time. Offloading it to a background job lets you respond to the client immediately (often with a "request accepted" response) while the actual work happens asynchronously, freeing up your API to handle other requests and giving you room to retry the background work independently if it fails, without that failure ever blocking or breaking the original API response.

2. What is a webhook, and how is it different from a client repeatedly polling your API for status updates?

A webhook is a callback, your backend makes an outbound HTTP request to a URL the client (or another system) provided, notifying it that something happened, rather than the client having to repeatedly ask "is it done yet." Polling wastes resources on both sides, most poll requests return "nothing new yet," and there's an inherent delay between when something actually happens and when the next poll happens to catch it. Webhooks push the notification the moment it's actually relevant, which is more efficient and lower-latency, at the cost of needing the receiving side to expose a reachable endpoint and handle potential delivery failures or retries from the sender.

3. What's the difference between a cron job and an event-driven background task?

A cron job runs on a fixed schedule, every night at 2 AM, every hour, regardless of whether there's actually anything new to process at that moment. An event-driven background task runs in direct response to something specific happening, a new order being placed, a file being uploaded, triggered immediately rather than waiting for the next scheduled window. You'd use a cron job for genuinely periodic, batch-style work (daily report generation, nightly cleanup), and event-driven processing for work that should happen as close to real-time as possible in response to a specific action.

4. How would you design a backend endpoint that kicks off a long-running process without making the client wait for it to fully complete?

The endpoint would validate the request, enqueue the actual work (onto a job queue or message broker), and immediately respond with a 202 Accepted status along with some kind of job or task ID the client can use to check progress later, rather than the client's original HTTP connection staying open for the full duration of the work. The client can then either poll a separate status endpoint using that ID, or, for a better experience, be notified via a webhook or a real-time channel (WebSocket, server-sent events) once the job actually completes.

5. What happens if a background job fails partway through, and how would you make sure it doesn't just silently fail?

Without explicit handling, a failed background job can just vanish, no error surfaces anywhere a human would actually see it, and whatever it was supposed to accomplish simply never happens, with no one aware until a user notices something's missing. A well-designed system logs the failure with enough context to debug it, retries the job automatically for transient failures (with a bounded retry count and backoff, not infinitely), and routes jobs that repeatedly fail to a dead-letter queue, a separate holding area for manual inspection, rather than either silently dropping them or endlessly retrying something that's never going to succeed on its own.


Backend Security Practices

1. What's the difference between input validation and sanitization, and why do you need both at the API layer?

Validation checks whether input meets expected rules, rejecting a request outright if it doesn't (an email field that isn't a valid email format, a required field that's missing). Sanitization actively cleans or transforms input to remove or neutralize potentially dangerous content, stripping HTML tags from a text field to prevent stored XSS, for example, rather than simply rejecting the input. You generally need both because some invalid input should just be rejected outright (validation), while other input might be structurally valid but still need to be made safe before being stored or rendered elsewhere (sanitization), they solve related but distinct problems.

2. How would you actually prevent SQL injection in your backend code, beyond just "use an ORM"?

The actual, root-cause fix is using parameterized queries (or prepared statements), which ensure user input is always treated strictly as data by the database, never as executable SQL, regardless of what characters it contains, most ORMs do this correctly by default, which is why "use an ORM" is common (if slightly imprecise) advice. The real risk that "use an ORM" glosses over is that some ORMs still let you drop into raw SQL or use string interpolation for dynamic query fragments (sorting by a user-supplied column name, for example), and if that fallback path concatenates user input directly into the query string instead of parameterizing it, you're just as vulnerable as if you'd never used an ORM at all.

3. How would you implement rate limiting on a backend API, and what would you actually track to enforce it?

You'd track request counts per client (identified by API key, user ID, or IP address) within a defined time window, commonly using a fast, shared store like Redis so the count stays accurate across multiple backend instances, not just tracked locally in one instance's memory. Common algorithms include a fixed or sliding window counter, or a token bucket that allows some burstiness while still enforcing an average rate over time. Once a client exceeds their allotted limit within the window, subsequent requests get rejected with a 429 Too Many Requests status, typically along with a Retry-After header telling well-behaved clients exactly when to try again.

4. Where should TLS/HTTPS termination happen in a typical backend architecture, at the load balancer or at the application server itself?

Most commonly, TLS termination happens at the load balancer (or a dedicated reverse proxy/API gateway) sitting in front of your application servers, decrypting incoming HTTPS traffic there, and often forwarding plain HTTP internally to the actual application servers within a trusted, private network. This centralizes certificate management in one place rather than every individual application server needing its own certificate configuration, and it offloads the real computational cost of encryption/decryption to infrastructure specifically built for it, some architectures with stricter internal security requirements do still re-encrypt traffic between the load balancer and application servers too, but that's a deliberate additional step, not the default baseline setup.

5. How would you manage secrets (API keys, database passwords) in a backend application without hardcoding them into your codebase?

Secrets should be injected at runtime through environment variables or a dedicated secrets manager (like AWS Secrets Manager, HashiCorp Vault, or a cloud provider's equivalent), never committed directly into source code or checked into version control, even in a private repository, since repository access, backups, and history all become an unnecessary exposure surface for something that should be tightly controlled. A dedicated secrets manager specifically adds real value beyond plain environment variables for production systems, centralized access control and auditing (who accessed which secret and when), and the ability to rotate a credential without needing a full code deployment just to pick up the new value.


Testing, Logging, Monitoring and Deployment

1. What's the difference between a unit test, an integration test, and an API/contract test for a backend service?

A unit test isolates a single function or class, mocking out its dependencies, to verify its logic in isolation, fast and focused. An integration test verifies that multiple components actually work together correctly, your code plus a real (or realistically simulated) database, for example, catching issues an isolated unit test can't see. An API/contract test verifies your API's actual external behavior, the shape and correctness of requests and responses, matches what consumers expect and depend on, which is particularly important for catching accidental breaking changes to a public or shared API contract that internal unit and integration tests might not directly cover.

2. What is structured logging, and why is it so much more useful than just scattering console.log statements throughout your code?

Structured logging outputs log entries as consistently formatted, machine-parseable data (commonly JSON), with well-defined fields, timestamp, log level, a request ID, relevant context, rather than arbitrary, free-form text strings. This matters enormously once you have any real volume of logs, structured logs can be efficiently searched, filtered, and aggregated by a log management system (tell me every error for this specific request ID across every service it touched), which is genuinely difficult or impossible to do reliably against a pile of inconsistent, free-text console.log output scattered without any shared format or convention.

3. What is a health check endpoint, and what should it actually verify beyond just "the server process is running"?

A health check endpoint (commonly something like /health or /healthz) is queried by load balancers, orchestrators (like Kubernetes), and monitoring systems to determine whether an instance is actually healthy and should keep receiving traffic. A shallow health check that only confirms the process is running and can respond to HTTP requests misses a lot of real, meaningful failure modes, a deeper health check should also verify the service can actually reach its critical dependencies, the database, essential downstream services, since a server that's technically "up" but can't reach its database is genuinely unhealthy and shouldn't keep receiving live traffic just because the process itself hasn't crashed.

4. What's the difference between logs, metrics, and traces, and why do you generally need all three for observability?

Logs are discrete, detailed event records, useful for understanding exactly what happened in a specific instance or request, but expensive to store and query at scale for broad, aggregate questions. Metrics are numeric measurements aggregated over time (request rate, error rate, latency percentiles), efficient to store and great for spotting trends and triggering alerts, but they don't tell you the specific detail of any one event. Traces follow a single request's full journey across multiple services, showing exactly where time was spent and where a specific failure actually occurred in a distributed call chain. You generally need all three because they answer different questions, metrics tell you that something's wrong (an elevated error rate), traces help you find where in a distributed system it went wrong, and logs give you the detailed why once you've narrowed down the specific request or component.

5. How would you manage environment-specific configuration (dev, staging, production) in a backend application without hardcoding values or duplicating config files?

The standard approach, aligned with 12-factor app principles, is to keep configuration entirely out of the codebase and inject it through environment variables (or a configuration service) that differ per deployment environment, the same application code and build artifact runs unchanged across dev, staging, and production, with only the injected configuration values actually differing between them. This avoids both hardcoding environment-specific values directly into code (which risks accidentally using a production credential in dev, or vice versa) and duplicating near-identical config files per environment (which inevitably drift out of sync with each other over time as one gets updated and the others are forgotten).



Struggling to Find a Job? Get Specific Batch Wise job Updates ✅ Check now

Join our WhatsApp Channel for more resources.