Software · Skills roadmap

Software Engineer (Backend) interview questions

Build the services, APIs and data that products run on, and keep them fast and reliable.

  • 36 to 60 weeks to job-ready, part time
  • 69 lessons across 21 skills
  • 12 portfolio projects

81 lessons and projects. Progress saves in this browser.

Try each question out loud before opening the answer. Shared questions (tagged by skill) also appear on other roles that use the same skill.

Concepts

  1. Beginner · REST APIs and HTTPWhat do the status codes 200, 201, 202, 400, 401, 403, 404, 409, 429 and 503 mean?

    200 OK, success with a body. 201 Created, a new resource was created. 202 Accepted, the request was received and will be processed later (common for webhooks and long jobs). 400 Bad Request, the input is invalid. 401 Unauthorized, missing or invalid credentials. 403 Forbidden, authenticated but not allowed. 404 Not Found. 409 Conflict, for example a duplicate or a version conflict. 429 Too Many Requests, you are rate limited. 503 Service Unavailable, a temporary server problem, often worth retrying later.

  2. Beginner · Automated testingWhat is the test pyramid, and how would you apply it to a backend service?

    Many fast unit tests at the bottom, fewer integration tests in the middle, and a small number of end-to-end tests on top. For a service: unit tests for business rules and edge cases; integration tests that call endpoints with a real database (for example in a container) to check SQL, serialisation and status codes; and a few end-to-end tests of the critical user journeys in a deployed environment. Lower levels are faster and more precise about what broke; higher levels catch wiring problems.

  3. Beginner · SQLWhat is the difference between WHERE and HAVING?

    WHERE filters rows before grouping; HAVING filters groups after aggregation. So WHERE fare_amount > 0 removes refunds before you count, while HAVING count(*) > 100000 keeps only busy zones after counting. Aggregates such as count(*) cannot appear in WHERE. Filtering in WHERE when you can is also faster, because fewer rows reach the grouping step.

  4. Beginner · SQLWhen can a LEFT JOIN return more rows than the left table has?

    When the right table has more than one match for a key. Each left row is repeated once per match. Check the key is unique on the right side (count(*) versus count(distinct key)) and compare row counts before and after the join. Duplicating a single zone in a lookup table, for example, inflated a 3.48 million row taxi join by about 170,000 rows.

  5. Beginner · Java and Spring BootWhat is the contract between equals and hashCode in Java, and what breaks if you violate it?

    If two objects are equal according to equals, they must return the same hashCode. Hash-based collections such as HashMap and HashSet first use the hash code to find a bucket, then equals to compare within it. If you override equals but not hashCode, two equal objects usually land in different buckets, so a HashSet can contain duplicates and map.get(key) can miss an entry you just put in. Records generate both methods consistently from their fields, which is one reason to use them for value objects.

  6. Beginner · Java and Spring BootWhat is the difference between checked and unchecked exceptions, and when would you use each?

    Checked exceptions (subclasses of Exception but not RuntimeException) must be declared or caught, so the compiler forces callers to deal with them; they suit recoverable conditions the caller can reasonably handle, such as a missing file. Unchecked exceptions (RuntimeException and subclasses) signal programming errors or conditions most callers cannot handle, such as invalid arguments or illegal state. Many modern codebases prefer unchecked exceptions with a global handler at the boundary, and use checked ones sparingly.

  7. Beginner · Java and Spring BootWhy prefer immutable objects, and how do records help?

    Immutable objects cannot change after construction, so they are safe to share between threads without locks, safe to use as map keys, and easier to reason about because no other code can modify them behind your back. A record declares its fields once and gets a canonical constructor, accessors, equals, hashCode and toString, with final fields. Note that a record holding a mutable list is only shallowly immutable; copy it with List.copyOf in the constructor.

  8. Beginner · Automated testingWhat is the difference between a mock, a stub and a fake?

    A stub returns canned answers to calls, so the code under test can run. A mock also records how it was called, so the test can verify interactions (called once with these arguments). A fake is a working but simplified implementation, such as an in-memory repository. Use them for slow or external boundaries such as networks, clocks and payment providers, and prefer real collaborators for your own simple code, since over-mocking makes tests pass while the real integration is broken.

  9. Intermediate · REST APIs and HTTPWhat is idempotency, and why does it matter when calling or building APIs?

    An operation is idempotent if doing it several times has the same effect as doing it once. Networks fail after a request is processed but before the response arrives, so clients retry, and non-idempotent operations such as "create payment" can then happen twice. GET, PUT and DELETE are idempotent by design; for POST, use an idempotency key: the client sends a unique key per logical operation and the server stores the result for that key, returning it again for repeats instead of acting twice.

  10. Intermediate · REST APIs and HTTP, TypeScript and JavaScriptHow do you secure a webhook endpoint?

    Verify every request: most providers sign the raw body with an HMAC and a shared secret, so recompute the signature over the exact bytes received and compare in constant time. Reject requests with old timestamps to limit replays, and store processed event ids so duplicates are ignored. Use HTTPS, limit the body size, validate the payload, and return 2xx quickly before doing slow work in the background. Keep the secret in a secrets manager and support rotating it.

  11. Intermediate · SQLExplain ROW_NUMBER, RANK and DENSE_RANK.

    For values 10, 9, 9, 7 ordered descending: row_number gives 1, 2, 3, 4 (always unique); rank gives 1, 2, 2, 4 (ties share, then a gap); dense_rank gives 1, 2, 2, 3 (ties share, no gap). Use row_number for "exactly one row per group", dense_rank for "Nth distinct value".

  12. Intermediate · REST APIs and HTTPWhich failed API requests should you retry, and how?

    Retry transient failures: network errors, timeouts, 429 (rate limited) and most 5xx responses. Do not retry 4xx client errors such as 400, 401, 403 or 404, because they will fail again. Use exponential backoff with jitter and a maximum number of attempts, and respect a Retry-After header when present. Only retry operations that are idempotent or protected by an idempotency key. Log each attempt with a request id, and send items that still fail to a dead-letter store for review.

  13. Intermediate · Java and Spring BootHow does a HashMap work internally?

    It keeps an array of buckets. A key's hashCode is spread and mapped to a bucket index; entries in the same bucket are kept in a linked list, which Java converts to a balanced tree when a bucket grows large (more than 8 entries in a big enough table). When the number of entries passes capacity times the load factor (0.75 by default), the table doubles and entries are redistributed. Lookups are O(1) on average and degrade when many keys collide. HashMap is not thread-safe; use ConcurrentHashMap for shared access.

  14. Intermediate · Java and Spring BootTwo threads increment a shared counter a million times each and the result is less than two million. Why, and how do you fix it?

    count++ is a read, an add and a write. Two threads can read the same value and both write back the same result, losing an update: a race condition. Fix it by making the update atomic: AtomicInteger.incrementAndGet(), a synchronized block, or a LongAdder for heavy contention. volatile alone is not enough, because it guarantees visibility, not atomicity. Better still, avoid shared mutable state, for example by having each thread count locally and combining at the end.

  15. Intermediate · Java and Spring BootWhat is dependency injection in Spring, and why prefer constructor injection?

    Spring creates and manages objects (beans) and supplies each one's dependencies instead of the object creating them itself. With constructor injection, dependencies are parameters of the constructor, so they are explicit, can be final, the object is always fully initialised, and tests can pass in fakes without Spring. Field injection with @Autowired hides dependencies, allows partly built objects and makes plain unit tests harder.

  16. Intermediate · NoSQL databases, SQLWhen would you choose a document database over a relational one?

    When the data is naturally a self-contained document read and written as a whole (a product with varied attributes, a user profile, an event), the schema varies between records, and the access patterns are known and mostly by key. Relational databases fit better when data has many relationships queried in many directions, when you need multi-row transactions and strong constraints, and when ad hoc reporting matters. Many teams use both, and PostgreSQL's JSON columns cover a lot of "flexible schema" needs.

  17. Intermediate · NoSQL databasesWhy does the order of fields in a compound index matter?

    A compound index is sorted by its first field, then the second within it, and so on, like a phone book sorted by surname then first name. A query can use the index efficiently only for a prefix of its fields. For a query that filters on equality, sorts, and filters on a range, the common guideline is equality fields first, then sort fields, then range fields, so the database can walk the index in order without a separate sort. Check with the query plan.

  18. Intermediate · Kafka and event streaming, MicroservicesWhen should services call each other synchronously, and when should they communicate with events?

    Use a synchronous call when the caller needs the answer to continue, for example checking stock before confirming an order, and keep the chain short with timeouts. Use events when other services need to react but the caller does not need to wait: sending a confirmation email, updating loyalty points, analytics. Events decouple services and absorb spikes, at the cost of eventual consistency, duplicate handling and harder debugging. A common design is synchronous for the critical path and events for everything that follows.

  19. Intermediate · SQL, System designYou add read replicas to a database. What new problem appears, and how do you handle it?

    Replication lag: replicas can be slightly behind the primary, so a user who just changed something may read the old value from a replica. Handle it by reading the user's own recent writes from the primary (for example for a few seconds after a write, or for specific pages), using session tokens that carry the latest write position, or accepting staleness where it does not matter. Replicas add read capacity only; writes still go to one primary.

  20. Intermediate · GoHow should errors be handled and wrapped in Go?

    Return errors as the last value and check them immediately. Add context when passing an error up with fmt.Errorf("loading user %d: %w", id, err), which keeps the original error for inspection. Callers test for a specific cause with errors.Is (sentinel errors) or extract a typed error with errors.As. Do not both log and return the same error, which duplicates logs, and avoid panic for expected failures.

  21. Advanced · MLOps, KubernetesWhat is the difference between liveness and readiness probes, and how would you set them for a model server?

    A liveness probe answers "is the process stuck?"; if it fails, Kubernetes restarts the container. A readiness probe answers "can this pod take traffic now?"; if it fails, the pod is removed from the Service endpoints without a restart. For a model server, make readiness pass only after the model has loaded, and keep liveness cheap and independent of downstream services, so slow model loading or a busy dependency does not cause restart loops. Use a startup probe or an initial delay when loading takes a long time.

  22. Advanced · SQLA dashboard query takes two minutes. How do you speed it up?

    Read the plan with EXPLAIN ANALYZE. Look for full scans of large tables, non-sargable filters (functions on indexed columns), joins on unindexed keys and row estimates far from reality. Fixes: filter early, select only needed columns, add or fix indexes, rewrite date filters as ranges, pre-aggregate into a summary table, and on warehouses use partitioning and clustering to cut bytes scanned. Measure before and after.

  23. Advanced · Kafka and event streamingHow do partitions and consumer groups work in Kafka?

    A topic is split into partitions; order is guaranteed only within a partition, and events with the same key always go to the same partition. Consumers in a group share partitions, each partition read by one member at a time, so parallelism is capped by the partition count. Each group commits offsets per partition to remember its position.

  24. Advanced · Kafka and event streamingHow do you avoid duplicates when a stream consumer crashes and restarts?

    Assume at-least-once delivery: process, then commit offsets. Make the sink idempotent with a unique event id and an upsert, or record processed ids in the same transaction as the result. Enable producer idempotence to avoid duplicates at the source, route poison messages to a dead-letter topic, and remember that side effects (emails, dead letters) also repeat on replay.

  25. Advanced · Java and Spring Boot, SQLWhat does @Transactional do, and what are common pitfalls?

    It wraps the method in a database transaction through a proxy: commit on normal return, roll back on unchecked exceptions (checked exceptions do not roll back by default). Pitfalls: calling a @Transactional method from another method in the same class bypasses the proxy, so no transaction starts; long transactions that include remote calls hold locks and connections; and assuming it protects against lost updates without the right isolation level or optimistic locking (@Version). Keep transactions short and around the database work only.

  26. Advanced · System designWhat is consistent hashing and why is it used?

    It places both servers and keys on a hash ring and assigns each key to the next server clockwise. When a server is added or removed, only the keys in its segment move, roughly one in n, instead of almost all keys as with hash(key) % n. Virtual nodes (many points per server) spread load evenly. It is used for sharding databases and distributing cache keys, for example in Cassandra and DynamoDB-style systems and in many client-side cache libraries.

Coding

  1. Beginner · Java and Spring Boot, PythonGiven a list of order amounts and a target, return whether any two different orders add up to the target. What is your approach and its complexity?

    The brute-force approach checks every pair: O(n squared) time, O(1) extra space. Better: walk the list once, and for each amount check whether target - amount is in a hash set of amounts seen so far, then add the current amount: O(n) time on average and O(n) space. If the list is already sorted, two pointers from both ends give O(n) time with O(1) space. State the trade-off and test edge cases: empty list, one element, duplicates and negative values.

  2. Intermediate · MLOps, Docker and containersYour model-serving Docker image is 6 GB. How would you make it smaller?

    Find what is taking the space with docker history or a tool such as dive. Then use a slim base image, a multi-stage build so compilers and build tools stay out of the final image, install only runtime dependencies (for example CPU-only framework wheels if you serve on CPU), clear package caches in the same layer, add a .dockerignore so data and notebooks are not copied, and keep large model files out of the image when they can be pulled from a registry or volume at start-up. Order layers so dependencies are cached before the application code.

  3. Intermediate · GoWhat is a goroutine leak, and how do you prevent one?

    A goroutine that never finishes, usually because it waits forever on a channel nobody will send to or receive from, or on I/O with no timeout. Leaked goroutines hold memory and resources until the process restarts. Prevent it by giving every goroutine a way to stop: pass a context.Context and select on ctx.Done(), close channels from the sender when done, use buffered channels where a result may be abandoned, and put timeouts on network calls. goleak and the runtime's goroutine count help detect leaks in tests.

Case studies

  1. Intermediate · CI/CD, Automated testingA test fails about one run in twenty in CI. How do you deal with it?

    Treat it as a bug, not bad luck. Reproduce it by running the test many times, in random order and in parallel. Common causes: shared state between tests (a database, static variables, files), dependence on time or timezone, unordered collections, real network calls, and sleeps used instead of waiting for a condition. Fix the cause: isolate state per test, inject a clock, wait on conditions with timeouts, and mock external calls. Quarantining without a fix only hides a real defect or a real race in production code.

  2. Intermediate · Java and Spring Boot, SQLAn endpoint that lists 50 orders with their items runs 51 SQL queries. What is happening and how do you fix it?

    That is the N+1 query problem: one query loads the orders, then lazy loading fires one query per order for its items. Fix it by fetching what the endpoint needs in one or two queries: a fetch join or entity graph in JPA, a batch fetch size, or a query that returns a DTO directly. Confirm with SQL logging or a test that asserts the number of queries, and watch pagination, because fetch joins on collections can break database-side paging.

  3. Intermediate · NoSQL databases, System designYou add a Redis cache for product pages. Prices sometimes show old values for minutes. How do you fix it?

    Delete (or update) the cached key whenever the price changes, in the same code path that writes to the database, and keep a TTL as a safety net. Be careful about ordering: write the database first, then delete the cache key, so a concurrent reader does not re-cache the old value; for strict cases, delete again shortly after or version the keys. If updates come from other systems, publish change events and invalidate from a consumer. Measure hit rate and staleness, and use shorter TTLs for data where staleness costs money.

  4. Advanced · Microservices, Monitoring and observabilityA downstream service becomes slow and your whole site starts timing out. What happened and how do you prevent it?

    Requests to the slow service held threads and connections while waiting, so the calling service ran out of them and started failing too, and the failure spread upstream: a cascading failure. Prevent it with timeouts on every call, a circuit breaker that fails fast when the dependency is unhealthy, bulkheads (separate pools per dependency), limited retries with backoff and jitter, and a fallback. Watch saturation metrics such as pool usage and queue length, not just error rates.

  5. Advanced · MicroservicesPlacing an order must reserve stock, charge a payment and create a shipment, each in a different service. How do you keep them consistent?

    Avoid distributed two-phase commit. Use a saga: a sequence of local transactions, each publishing an event (or driven by an orchestrator), with a compensating action for each step, such as releasing the stock if payment fails. Publish events reliably with an outbox, make every step idempotent, and model intermediate states explicitly (payment pending) so users see something truthful while the saga runs. Monitor stuck sagas and have a way to retry or fix them.

System design

  1. Intermediate · System designDesign a URL shortener.

    Clarify requirements (create, redirect, expiry, custom codes, analytics?) and estimate: for example 100 million new links a month is about 39 writes per second and, at 100 reads per write, about 3,900 redirects per second, with about 3 TB over five years at 500 bytes per link. Design: stateless servers behind a load balancer; a key-value table keyed by code; a cache for popular links. Deep dives: code generation (random base-62 with a uniqueness check, or an encoded counter), 301 versus 302, abuse prevention and what happens when the cache or database fails.

  2. Advanced · NoSQL databases, System designDesign a rate limiter for a public API.

    Clarify: limits per user, per API key or per IP; hard or soft; global or per region. Algorithms: fixed window (simple, bursty at edges), sliding window log or counter (smoother), token bucket (allows bursts up to a size, common in practice). Store counters in Redis with atomic operations or a Lua script so all API servers share state; return 429 with Retry-After. Discuss failure: if Redis is down, fail open or closed per endpoint; and latency: keep the check to one round trip, or use local approximations synchronised periodically.

Behavioural

  1. Intermediate · CommunicationHow do you explain a technical finding to a non-technical audience?

    Lead with the decision or finding, use one number with context, and replace method with an analogy ("we scored customers on how recently, how often and how much they buy"). Show one clear chart titled with the takeaway. Keep methods in an appendix and check understanding by asking what they would do next.

  2. Intermediate · Communication, Stakeholder managementTell me about a time a stakeholder disagreed with your analysis.

    Use situation, task, action, result. Show that you first understood their view (often a different definition or context), checked your own work, found the source of the difference together and agreed on the next step. End with the outcome and what you changed in how you work, for example writing definitions into every brief.

  3. Intermediate · Communication, Monitoring and observabilityTell me about a production issue you handled.

    Use a STAR story. Show how you detected it (alert, user report), how you limited the damage first (rollback, feature flag, scaling), how you found the root cause with evidence (logs, metrics, traces), how you communicated during the incident, and what changed afterwards (a test, an alert, a runbook, a design fix) as part of a blameless review. Interviewers look for calm prioritisation and learning, not heroics.

  4. Advanced · Stakeholder managementThree teams need analysis from you this week. How do you prioritise?

    Ask each team what decision depends on the work and by when. Rank by impact and deadline, share the proposed order with the requesters and your manager, and make trade-offs explicit ("I can do A and B this week, C next Tuesday"). Offer smaller versions where a rough answer is enough. Agree it in writing so nobody is surprised.