All jobs / Backend developer

Backend Languages interview questions

Backend Languages questions a Backend developer can be asked, from first job to senior. Open one to see the strongest answer and why it works.

  1. def add(item, bag=[]): bag.append(item); return bag — you call add(1) then add(2). What comes back the second time?

    The strongest answer: [1, 2] — the default list is created once when the function is defined, so every call without a bag shares it; use bag=None and build a new list inside

    Why it works: Exactly. One default object, shared for the life of the function. None plus a fresh list inside is the fix.

    • Default arguments are evaluated once, at definition.
    • A mutable default is shared by every call.
    • Use None and create the value inside the function.
    • The bug is silent, not an error.
  2. You pass a class instance and a struct to a method and both are modified inside. What does the caller see?

    The strongest answer: The class change is visible because the reference points at the same object; the struct was copied, so the caller's copy is untouched unless it was passed with ref

    Why it works: Exactly. Reference types share the object, value types are copied.

    • class = reference type, struct = value type.
    • The reference is copied; the object is not.
    • ref / out change the parameter binding itself.
    • Mutable structs are a known source of surprises.
  3. When would you choose a tuple over a list?

    The strongest answer: When the collection is fixed and its position means something — a coordinate, a record, a dict key — since tuples are immutable and therefore hashable

    Why it works: Right. Immutability is the point, and hashability is what it buys you.

    • Tuple = fixed, positional, hashable.
    • List = a sequence you intend to change.
    • Only a tuple can key a dict or join a set.
    • Choose on meaning, not on length.
  4. What is the most common cause of a NullReferenceException in a new codebase?

    The strongest answer: A reference that was never assigned or came back null from a lookup and was used without a check — nullable reference types and a null check at the boundary are what prevent it

    Why it works: Right, and enabling nullable reference types moves most of these to compile time.

    • Something was null and was dereferenced.
    • Enable nullable reference types.
    • Check at the boundary, not everywhere.
    • ?. and ?? express intent clearly.
  5. What is the difference between == and is?

    The strongest answer: == asks whether two values are equal; is asks whether they are the very same object — use is only for None, True, False and sentinels

    Why it works: Exactly. Identity versus equality, and is belongs to singletons.

    • == compares values, is compares identity.
    • Use is None, not == None.
    • Equal objects need not be the same object.
    • Caching makes is look right by accident.
  6. When do you use an array rather than a List<T>?

    The strongest answer: When the size is fixed and known, or an API demands one — List<T> grows, exposes Add and Remove, and is what most code should take and return

    Why it works: Right. Fixed size is the reason; otherwise List<T> says more about intent.

    • Array = fixed size.
    • List<T> = grows, richer API.
    • Accept IEnumerable<T>, return a concrete type.
    • Span<T> for slicing without copying.
  7. You want a value from a dict, or a default if the key is missing, without raising. What do you use?

    The strongest answer: d.get(key, default) — or d.setdefault / defaultdict when the missing key should also be stored

    Why it works: Right. get for a read; setdefault or defaultdict when the miss should create the entry.

    • get(key, default) for a safe read.
    • setdefault / defaultdict when the miss should stick.
    • in then index looks up twice.
    • or swallows falsy stored values.
  8. You remove items from a List<T> inside a foreach over it and get an exception. Why?

    The strongest answer: The enumerator detects that the collection changed and throws — iterate a copy, use RemoveAll with a predicate, or loop backwards by index

    Why it works: Exactly. RemoveAll is usually the clearest of the three fixes.

    • Modifying during enumeration invalidates it.
    • RemoveAll(predicate) in one call.
    • Or iterate a copy, or go backwards by index.
    • The same rule applies to Dictionary.
  9. if items: — what exactly is being tested?

    The strongest answer: Whether items is truthy: an empty list, empty string, empty dict, 0 and None are all falsy, so this is not the same as testing for None

    Why it works: Exactly — which is why an empty list and None take the same branch unless you separate them.

    • Empty containers, 0 and None are all falsy.
    • if x is not None when None is the thing you mean.
    • if x when any empty value should take the same branch.
    • Be explicit when the two differ.
  10. When is var a good idea?

    The strongest answer: When the type is obvious from the right-hand side and the name carries the meaning; when it is not obvious, spelling the type out helps the reader more than it costs

    Why it works: Right. It is a readability judgement, and reviewers are the audience.

    • var when the type is evident.
    • Spell it out when it is not.
    • It is static typing either way.
    • Required for anonymous types.
  11. Building a long string in a loop with s += piece is slow on big inputs. Why?

    The strongest answer: Strings are immutable, so each += builds a whole new string and copies what came before; collect the pieces in a list and "".join(parts) once

    Why it works: Right. Copy per iteration versus one allocation at the end.

    • Strings cannot be changed in place.
    • += in a loop copies repeatedly.
    • Collect into a list, then join.
    • The cost grows with the square of the length.
  12. Why expose a property rather than a public field?

    The strongest answer: A property is a method pair, so you can add validation, change the backing store or make it read-only later without breaking callers or their compiled binaries

    Why it works: Exactly. It is about being able to change your mind later.

    • A property is a method pair.
    • You can add validation later.
    • Changing a field to a property is a binary break.
    • Auto-properties cost nothing to write.
  13. What does xs[1:4] give you, and what does xs[:] do?

    The strongest answer: Items at index 1, 2 and 3 — the stop is exclusive — and xs[:] is a shallow copy of the whole list

    Why it works: Exactly. Half-open ranges, and a bare slice copies the outer list only.

    • Stop is exclusive: [1:4] is 1, 2, 3.
    • xs[:] is a shallow copy.
    • Slices clamp instead of raising.
    • Nested objects are still shared after a shallow copy.
  14. const versus readonly — what is the practical difference?

    The strongest answer: const is baked into the callers at compile time, so changing it in a library needs every consumer rebuilt; readonly is set at run time and can differ per instance or come from configuration

    Why it works: Right, and the cross-assembly baking is the one that bites in practice.

    • const is inlined into callers.
    • readonly is assigned at run time.
    • readonly does not make the object immutable.
    • Prefer readonly across assembly boundaries.
  15. You need both the index and the item while looping. What is the idiomatic way?

    The strongest answer: for i, item in enumerate(items) — and enumerate(items, start=1) when the count should begin at one

    Why it works: Right, and the start argument saves the off-by-one arithmetic.

    • enumerate gives index and item together.
    • start=1 for human-facing numbering.
    • range(len(x)) is a smell in review.
    • Manual counters drift.
  16. Why code against an interface rather than a concrete class?

    The strongest answer: So callers depend on the capability rather than the implementation — which is what lets you substitute a fake in a test or swap the implementation without touching them

    Why it works: Right, and testability is usually the first payoff people feel.

    • Depend on the capability, not the implementation.
    • Substitution is what makes testing possible.
    • Do not add an interface with only one forever-implementation by reflex.
    • Keep interfaces small.
  17. When is a list comprehension the right choice over a for loop?

    The strongest answer: When you are building one collection from another with a simple expression and filter; once it needs branching, side effects or two levels of nesting, a loop reads better

    Why it works: Exactly. It is a readability call, and the line where it flips is nesting.

    • Build-from-iterable with a simple expression.
    • Side effects belong in a loop.
    • Two levels of nesting is the readability limit.
    • A generator expression when you do not need the list.
  18. What is wrong with catch (Exception ex) around a whole method?

    The strongest answer: It catches the failures you cannot handle along with the one you can, hiding bugs and leaving the object in an unknown state — catch what you can act on, and let the rest reach a top-level handler that logs

    Why it works: Exactly, and a top-level handler is what makes narrow catches affordable.

    • Catch what you can actually handle.
    • A broad catch hides bugs.
    • Let a top-level handler log the rest.
    • throw; preserves the stack, throw ex; loses it.
  19. rows2 = rows[:] then rows2[0].append(9). Why did rows change too?

    The strongest answer: The slice copied the outer list but not the inner lists, so both lists hold the same row objects — copy.deepcopy, or build new inner lists, if you need them separate

    Why it works: Right. One level deep is exactly what a shallow copy means.

    • A slice copies one level.
    • Inner objects stay shared.
    • deepcopy, or rebuild the inner values.
    • The same trap applies to dicts of dicts.
  20. What does a using statement guarantee?

    The strongest answer: That Dispose runs when the block exits, exception or not — which is how files, connections and HTTP responses get released instead of waiting for the garbage collector

    Why it works: Right. Deterministic cleanup is exactly what the collector cannot give you.

    • Dispose runs on every exit path.
    • The GC does not free unmanaged handles promptly.
    • using declarations scope to the enclosing block.
    • await using for IAsyncDisposable.
  21. What is wrong with except: pass?

    The strongest answer: It swallows everything including typos, KeyboardInterrupt and bugs you needed to see; catch the specific exception you can actually handle, and log anything you deliberately ignore

    Why it works: Exactly. It hides the failures that would have told you what was wrong.

    • Catch the exception you can handle.
    • A bare except hides typos and interrupts.
    • Log anything you choose to ignore.
    • except Exception is broad; bare except is broader.
  22. You compare user input to a stored value with ==. What could go wrong?

    The strongest answer: Culture and case: == is ordinal and case-sensitive, so compare with an explicit StringComparison — OrdinalIgnoreCase for identifiers, culture-aware only for text a human is sorting or displaying

    Why it works: Exactly. Being explicit about the comparison is the habit worth forming.

    • == on string is ordinal, case-sensitive.
    • OrdinalIgnoreCase for identifiers and keys.
    • Culture-aware only for human-facing text.
    • Culture bugs appear only on some machines.
  23. The interviewer asks: “In ASP.NET, what belongs in an API controller?” Strongest answer?

    The strongest answer: HTTP in and out: bind the route and verb, validate the request, call an application service, return a status and a DTO. Business rules stay out of the action.

    Why it works: Exactly. Controllers are the HTTP adapter. Domain rules live in services (or the domain model) so you can test them without Kestrel.

    • Controller = HTTP adapter.
    • Validate, call a service, return a DTO.
    • Keep business rules out of the action.
    • Never exec client SQL.
  24. You add a database starter to a Spring Boot project and a connection pool appears without you configuring anything. What is doing that, and how do you take control of it?

    The strongest answer: Auto-configuration: Spring Boot creates beans conditionally on what is on the classpath and which properties are set, and backs off when you declare your own — so you override by defining the bean or setting the property

    Why it works: Right — conditional beans plus back-off is the whole mechanism, and the condition report printed in debug mode tells you which ones fired.

    • Conditional beans that back off.
    • Classpath and properties drive the conditions.
    • Define your own bean to win.
    • Read the auto-configuration report.
  25. When does a finally block run?

    The strongest answer: On the way out of the try either way — after a successful run, after a handled or unhandled exception, and on return or break — which is why it is where cleanup goes

    Why it works: Right. That guarantee is the whole point of the construct.

    • finally always runs on the way out.
    • except handles, else runs on success.
    • Cleanup belongs in finally, or in a with block.
    • It runs on return and break too.
  26. What does int? give you that int does not?

    The strongest answer: The ability to represent absent — a value type that may have no value, with HasValue and Value, so a missing database column or unanswered field is not forced to be zero

    Why it works: Right, and using 0 or -1 as a sentinel is exactly what this replaces.

    • int? represents a missing value.
    • Sentinels like 0 or -1 lie.
    • GetValueOrDefault and ?? to unwrap.
    • Nullable reference types do the same for classes.
  27. The interviewer asks: “What does a Spring @RestController actually do?” Strongest answer?

    The strongest answer: It maps HTTP to methods. Keep it thin: validate, call a service, return a representation — don't open JDBC in the method.

    Why it works: Exactly. The controller is the web adapter. Transactions and persistence belong one layer down, with a clear boundary.

    • RestController maps HTTP to a thin method.
    • Call a service; don't open JDBC there.
    • Transactions belong below the web layer.
    • Don't serialise managed entities by accident.
  28. The interviewer asks: “What does a good Express (or Fastify) route handler look like?” Strongest answer?

    The strongest answer: Read the request, validate, await the service, send a status and JSON. Don't swallow errors — pass them to the error middleware so failures become a safe 5xx.

    Why it works: Exactly. Async I/O, validation, and a single error path. Unhandled promise rejections are 500s you never logged.

    • Validate, await, respond, don't swallow.
    • Use error middleware for failures.
    • Don't block the event loop.
    • Never eval request input.
  29. The interviewer asks: “How should a FastAPI path operation be structured?” Strongest answer?

    The strongest answer: Declare the route, verb and response model. Let Pydantic validate input; the function calls a service and returns a model — I don't grow SQLAlchemy queries inside the route.

    Why it works: Exactly. The path operation is the HTTP edge. Types and models catch bad input; services own the use case.

    • Path ops are the HTTP edge.
    • Pydantic validates; a service does the work.
    • Keep the response model explicit.
    • Never execute client SQL.
  30. A Node service that normally answers in 20 ms starts timing out on every endpoint whenever one particular report is requested.

    What do you suspect first?

    The strongest answer: Something on that path runs synchronously — a long loop, a large JSON parse, a sync file or crypto call — and it blocks the single thread that runs your JavaScript, so every other request waits behind it

    Why it works: Exactly — one thread runs your code, so a synchronous stretch stalls everyone, not only the caller.

    • One thread runs your code.
    • Find the synchronous stretch.
    • Move CPU work off the loop.
    • One slow path stalls every path.