Loading...
Loading...
Backend interview guide
Scaling, caching, databases, queues, load balancing and designing common systems. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
61 questionsLast updated
System design is deciding the main parts of a software system, how they communicate and how they keep working as load grows. It starts from requirements and numbers, and every choice is explained with its trade-off.
Lesson: What system design is (and is not)Functional: a customer can place an order, and a customer can track an order's status. Non-functional: product pages respond in under 300 ms for 95% of requests, and checkout is available 99.9% of each month.
Lesson: What system design is (and is not)I ask a few questions: who the users are, which features matter most, and roughly how many users. Then I write a short list of functional requirements, an out-of-scope list and NFRs with numbers. If they give no numbers, I state an assumption such as 1 lakh daily users and move on.
Lesson: What system design is (and is not)Yes, if I say so clearly. An out-of-scope list keeps the design small enough to finish in the time, and it shows I understand priorities. I can mention how a left-out feature would fit later if there is time.
Lesson: What system design is (and is not)The average mixes many fast requests with a few slow ones, so it can look fine while some users wait seconds. p99 shows the time 99 in 100 requests beat, so it tells you how bad it gets for the slowest users. At high traffic, 1% is still thousands of people.
Lesson: Latency, throughput and availabilityThey are in series, so I multiply: 0.999 × 0.999 ≈ 99.8%. The chain is always weaker than its weakest part. To improve it I would add copies of both, because copies in parallel are down only when all of them fail together.
Lesson: Latency, throughput and availabilityTail latency is the slowest part of the distribution, p99 and above. If a page waits for many calls, it is slow whenever any one call is slow, so the chance grows as 1 − 0.99ⁿ. With 100 calls, a 1% slow rate per call makes most page loads slow.
Lesson: Latency, throughput and availabilityIt starts as one server with the app and database. Then the database gets its own server, several app servers go behind a load balancer, a cache, read replicas and a CDN take load off the database and network, and finally queues with workers move slow work out of requests. Each step is driven by a measured bottleneck.
Lesson: Single server to many servers: how systems growThroughput is limited by the busiest resource. Once the app servers have spare capacity, the database or the network becomes the bottleneck, and extra app servers just wait on it. Then you need a cache, replicas or query tuning instead.
Lesson: Single server to many servers: how systems growKeep a standby copy that receives every change from the primary and can be promoted if the primary dies, and take regular backups. The app connects through an address that can be switched to the new primary, so failover needs no code change.
Lesson: Single server to many servers: how systems growEach stage adds cost, more machines to run and more failure points, and it solves a problem that a small shop does not have yet. I would start with one server, measure, and add the step that fixes the busiest resource, so every box has a reason.
Lesson: Single server to many servers: how systems growI use one sentence: we chose X, we gave up Y, because Z. The cost is said openly, and the reason points to a requirement or a number, so the interviewer can see I picked it deliberately.
Lesson: Trade-offs and a step-by-step approach for design roundsI clarify features and numbers for about 5 minutes, estimate scale for 5, define the main API and the data model in about 10, draw the high-level design for 10, then deep dive into the hardest part and its bottlenecks. I keep the last few minutes for trade-offs and what changes at 10x load.
Lesson: Trade-offs and a step-by-step approach for design roundsEach part adds cost, failure points and work, so it needs a reason from the numbers. If the estimate says a few requests a second, one app and one database meet every requirement, and I can describe when I would add a cache or split services.
Lesson: Trade-offs and a step-by-step approach for design roundsMixing features and qualities in one list, choosing a number of nines without counting the downtime minutes, forgetting single points of failure such as one load balancer or two critical parts on one machine, and jumping to tools with no requirements, numbers or trade-off.
Lesson: System design thinking practiceAbout 43 minutes in a 30-day month. It suits most shops and college apps; payments or emergency services need more nines, and staff-only tools can live with less.
Lesson: System design thinking practiceThe average hides the slow tail. p99 tells me the time 99 in 100 requests beat, so I know how bad it gets for real users, and at high traffic that 1% is many people.
Lesson: System design thinking practiceNot necessarily. The app tier is, but if there is one load balancer and one database, either can stop everything. I would add a second load balancer and a standby database before calling it highly available.
Lesson: System design thinking practiceLayers organise code inside one program, so calls between them are cheap method calls. Tiers are parts of the deployed system on separate machines, such as client, app servers and database, so calls between them cross the network.
Lesson: Layered architecture at system levelIt skips the service, so business rules such as hiding inactive products or checking stock are not applied. It also spreads data access across HTTP code, which makes the rules hard to find and easy to break. A follow-up you may get: "What if the controller only reads?" It still skips rules such as hiding inactive products, so it still goes through the service.
Lesson: Layered architecture at system levelWith filters or interceptors that wrap every request, like Spring Security's filter chain. They run before and after the controller, so the rule exists in one place and new endpoints get it automatically. Spring Security's filter chain is the real version of this idea.
Lesson: Layered architecture at system levelIt is still one deployable, but the code is split into business-area modules such as catalogue, orders and payments. Each module exposes a small public API and hides its internals and tables, and other modules may only use that API. A follow-up: "How is it different from microservices?" It is still one deployable and one database, so calls stay local and deploys stay simple.
Lesson: Monolith and modular monolithI would make one direction an event. For example, orders publishes OrderPlaced and notifications listens to it, so orders no longer needs to call notifications and the dependency becomes one-way. This is also how a modular monolith avoids cycles.
The module APIs are already the service contracts. To move the catalogue out, I write an implementation of CatalogueApi that calls the new service over HTTP, and the orders module does not change.
Calls become network calls that are slower and can fail, so partial failure must be handled everywhere. There is no single transaction across services, so data consistency needs extra work. And you need logs, tracing, monitoring and a deploy pipeline for every service.
Lesson: Microservices: what they are and why not to start with themWhen there are clear signals: several teams slowing each other down in one codebase, frequent deploy clashes, a part with very different load or compliance needs, and the ability to operate services. Then I split one module at a time along an existing boundary.
Lesson: Microservices: what they are and why not to start with themThe order service cannot tell whether the customer was charged. I would mark the order as payment unknown, check the payment status later or retry with an idempotency key, and release reserved stock if the payment really failed. A follow-up: "How do you retry safely?" Send a unique request id, so a second charge with the same id is ignored.
Lesson: Microservices: what they are and why not to start with themBecause they are interchangeable. I can add servers for a sale, remove them after and restart them for deploys without losing carts or logins, since none of that lives on the servers. The load balancer can send any request anywhere.
Lesson: Stateless application serversThe server puts the user id and roles in a token and signs it with a secret all servers share. The client sends it with each request, and any server can verify the signature without looking up a session, so no session store is needed for login.
Lesson: Stateless application serversIn object storage such as S3 or a shared file store, with only the file key saved in the database. A server's local disk is per-server state: other servers cannot see the file and it is lost when the server is replaced.
Lesson: Stateless application serversThe producer is the code that creates work, like checkout adding "send email for order 501". The queue stores those messages in order. The consumer is a worker that takes messages and does the work, at its own pace, separately from the request.
Lesson: Synchronous calls vs background jobs and queuesA separate queue for messages that still fail after the allowed number of retries. It stops one bad message from blocking or looping forever, and someone can inspect and replay those messages later.
Lesson: Synchronous calls vs background jobs and queuesFor a small ShopLite a jobs table or RabbitMQ is enough: simple background jobs with retries. Kafka shines for high-volume event streams that many consumers read and replay, which ShopLite does not need yet.
Lesson: Synchronous calls vs background jobs and queuesA stateless server keeps no user data in its own memory between requests; state is in a shared store or a signed token. It matters because then any server can handle any request, so servers can be added, removed and restarted freely.
Lesson: Backend architecture practiceI look for data kept between requests in memory or on local disk: static maps, HTTP sessions, caches, upload folders and scheduled jobs. For each one I ask whether the next request could land on another server, and move it to a shared store, a token or a lock if needed.
Lesson: Backend architecture practiceA message handler where processing the same message twice has the same effect as once. Usually it records each message id it has handled and skips repeats, which is needed because most brokers deliver at least once.
Lesson: Backend architecture practiceAn operation is idempotent if doing it several times has the same effect on the server as doing it once. GET, PUT and DELETE are idempotent by definition; POST is not, because each call can create something new.
Lesson: Idempotency keys for safe retriesIdempotency is about the server state, not the response. After one DELETE or two, the item is gone, so the effect is the same even though the second response says it was not found.
Lesson: Idempotency keys for safe retriesThat is a client bug, not a retry, so I reject it, typically with 422. I store a hash of the first body with the key and compare it on every repeat, so I never replay a response for a different cart.
Lesson: Idempotency keys for safe retriesWith cursor, or keyset, pagination on an indexed column: WHERE id > :lastId ORDER BY id LIMIT n. Every page then reads about n rows through the index, whereas a deep OFFSET would make the database walk past millions of rows.
Offsets are counted at the time of each request. If a new item is added at the top after page 1, every row shifts down one place, so page 2 starts with an item the user already saw. A cursor avoids this because it asks for rows after a specific item.
Lesson: Pagination for very large listsOtherwise a client can ask for a million items and bring back the unbounded-list problem. The server uses a default such as 20 when no size is sent and quietly caps large values at a maximum such as 100.
Lesson: Pagination for very large listsWithout one, a slow or dead service can hold my request threads for a long time. When the thread pool is used up, even endpoints that never call that service stop answering. A timeout turns a hang into a quick, handled error.
Lesson: Timeouts and retries with backoffTimeouts, refused connections, 502, 503 and 504, and 429 after its Retry-After delay. I never retry 4xx errors like 400 or 404, and I retry a POST only when it carries an idempotency key.
Lesson: Timeouts and retries with backoffWhen several layers each retry, one user action can become dozens of calls to a service that is already failing, which keeps it down. I retry at one layer only, limit attempts, use backoff with jitter, and add a circuit breaker or a retry budget.
Lesson: Timeouts and retries with backoffRemoving or renaming a field, changing its type or meaning, making an optional field required and adding a new required request field. Adding endpoints, optional parameters and new response fields is safe, as long as clients ignore fields they do not know.
Lesson: Backward-compatible API changesI usually prefer path versioning like /api/v1: it is visible in logs and links, simple to route and easy to test. Header versioning keeps URLs stable and can be cleaner, but it is harder to see, debug and cache. Either way, the version changes only for breaking changes.
Add a new field with the new shape in the current version, so new apps can switch while old apps keep reading the old field. Then publish a new version where the field has its final shape, run both, and retire the old version once traffic has moved.
Lesson: Backward-compatible API changesThe POST returns 202 Accepted with a job id and a Location header for the status endpoint. A worker builds the report in the background and updates the job's state and progress. The client polls GET /jobs/{id} and downloads the file from a result link once the status is DONE.
Lesson: Handling slow work asynchronouslyPENDING when accepted, RUNNING with a progress value, then DONE with a result link or FAILED with a clear error. States only move forward, so a finished job is never run twice by mistake.
Lesson: Handling slow work asynchronouslyWith an ExecutorService: the handler submits a task and returns immediately, and pool threads do the work and update the job record. In production I'd usually put jobs on a queue so they survive restarts and can be processed by separate workers.
Lesson: Handling slow work asynchronouslyAn operation is idempotent if repeating it leaves the same server state. It matters because clients retry after timeouts and users double-tap; without idempotency those repeats create duplicate orders or payments.
Lesson: API design at scale practiceTimeouts, 429 and 502, 503, 504, with exponential backoff, jitter and a small maximum number of attempts. I never retry 4xx validation errors, and I retry a POST only with an idempotency key.
Lesson: API design at scale practiceThe key must be claimed atomically, with a UNIQUE constraint or computeIfAbsent. The first request does the work; the second sees the key in progress and gets 409 or, after completion, the stored response.
For work that takes longer than a normal request, like exports or bulk imports. The server returns 202 with a job id and a status URL, does the work in the background, and the client polls or gets a webhook when it is done.
Lesson: API design at scale practiceA cache is a smaller, faster store that keeps copies of data that is read often. Reads that hit the cache come from memory or a nearby server instead of a slow database or a far network, so they are much faster and the database does less work.
Lesson: Where caches live: browser, CDN, application, databasemax-age and an ETag?max-age lets the browser reuse its copy without sending any request until the time runs out. An ETag is used after that: the browser asks "has it changed?" with If-None-Match, and the server answers 304 with no body if it has not. So max-age saves the request, and an ETag saves the body.
Secrets such as tokens, OTPs and payment details should never be cached, and I would mark them no-store. Per-user data like orders or addresses must not go into a shared cache without the user in the key. Live stock during a sale changes too fast, so I would re-check it in the database when the order is placed.
A cache holds copies, usually only of the hot data, and it may lose them on a restart or when memory is full. The database is the source of truth that keeps every record safely on disk with transactions. The cache only speeds up reads in front of it.
Lesson: Where caches live: browser, CDN, application, databaseWrite-through writes the database and the cache together, so both are up to date when the write returns. Write-behind writes only the cache and saves to the database later in batches. Write-behind is faster for writes but can lose data if the cache fails before it flushes.
Lesson: Cache-aside, write-through and expiryWhen data is read right after it is written and old values are costly, for example a user's profile settings shown again right after saving. Every write costs a bit more, but the next read is a guaranteed hit with the new value.
Lesson: Cache-aside, write-through and expiryEach lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson