Software design interview guide

Every question carries the same frame: the codebase situation behind it, what it is actually testing, what a strong answer sounds like, and the flags on both sides. The red flags are specific wrong answers a confident engineer really gives — a recited principle applied where it does not hold, not a gap in vocabulary.

17 of 17 questions
When should you introduce an interface?Abstraction

A service has fourteen classes and fourteen interfaces, each implemented exactly once, each named `IThing` for a class named `Thing`. A new engineer asks why, and the answer on the team is "so it is testable and loosely coupled". Meanwhile the one place with genuine variation — three payment providers — is a switch statement inside a 600-line class.

▶ code to react to
Pick one SOLID principle, tell me the problem it was invented to solve, and tell me a case where following it made a codebase worse.SOLID

A team has an architecture review checklist whose first item is "does this comply with SOLID?". Reviews have become arguments about whether a class has one responsibility, and the codebase has grown a large number of very small classes that are individually simple and collectively hard to follow.

You have to change a ten-year-old billing module with no tests, and the person who wrote it has left. Walk me through how you make the change safely.Legacy

The module is 4,000 lines across three files. It computes invoices, applies discounts, handles proration and writes to two tables. Finance depends on its output being byte-identical for existing customers. The requested change is to add a new discount type.

▶ code to react to
How do you decide which design pattern to use for a new feature?Patterns

A team is building a notification system that must send email now and, according to the product manager, "probably Slack and SMS at some point". The tech lead has proposed a design document listing Factory, Strategy, Observer and Template Method. No code exists yet.

▶ code to react to
Is technical debt always bad?Debt

An engineering manager wants a "debt sprint" every quarter. The backlog item list includes "migrate to the new ORM", "the auth module is ugly", "we still use callbacks in two files" and "the invoice generator has no tests and finance has been wrong twice".

An `Order` class has `isPaid`, `isCancelled`, `isShipped` and `isRefunded` as booleans. What is wrong with that, and what would you do instead?State

The flags were added one at a time over two years. There are now nineteen places that read some combination of them, three that write more than one in sequence without a transaction, and a support ticket where a customer received a refund for an order that was never paid.

▶ code to react to
Why would you inject a dependency rather than construct it inside the class that uses it?Dependencies

A service class constructs its own database client, HTTP client, clock and logger in its constructor. It has one test, which starts a real database in a container and takes ninety seconds. A new engineer has proposed adding a dependency injection container to the project.

▶ code to react to
The team wants to rewrite a service from scratch. How do you decide whether that is the right call?Decisions

A five-year-old order service in an older framework version. It works, it is in production, it handles about a hundred thousand orders a month, and everyone hates it. Two engineers have volunteered to rewrite it in three months. The original team is gone.

Two modules are coupled. How do you tell whether that is a problem?Coupling

A reviewer has objected to a pull request on the grounds that it "increases coupling". The change makes the checkout module call a function on the pricing module. The reviewer suggests publishing an event instead.

What is cohesion, and how do you tell a cohesive module from a folder full of related files?Decomposition

A codebase is organised as `controllers/`, `services/`, `repositories/` and `models/`. Adding a field to a customer profile touches one file in each, plus a validator and a serializer. The team describes the structure as "well separated".

A colleague sends a pull request that renames twenty variables and functions and changes no behaviour. Is that a good use of review time?Naming

The renamed things include `data` to `pendingInvoices`, `flag` to `isTaxExempt`, `process()` to `chargeAndEmail()`, and `timeout` to `timeoutMs`. One reviewer says naming is bikeshedding and the diff is noise.

▶ code to react to
How do you decide whether a failure should be an exception, a returned error value, or something the type system forces the caller to handle?Errors

A payment endpoint currently throws for everything: card declined, provider timeout, invalid amount, and a null dereference in the discount calculator. The controller catches `Exception` at the top and returns a 500. Support cannot tell declined cards from outages.

What does making a data structure immutable actually buy you, and what does it cost?Effects

A team has a `Cart` object mutated in place by fifteen call sites. A bug report says a discount is sometimes applied twice. Someone has proposed making `Cart` immutable, and someone else has objected that it will allocate too much.

When is a rich domain model worth the cost, and when is a transaction script the better design?Domain

A team is building an internal admin tool: twelve CRUD screens, a permissions check, and one screen that computes commission with about forty lines of rules that change quarterly. The tech lead wants entities, value objects, aggregates, repositories and domain services throughout.

How do you decide where an aggregate boundary goes?Domain

An `Order` has line items, a shipping address, a payment record and a review. Loading an order currently loads all four. A report screen times out because it loads ten thousand orders to sum their totals.

Where should the boundaries in a codebase go, and how do you know one is in the wrong place?Boundaries

A service follows a layered structure with domain, application, infrastructure and presentation packages, plus an interface and a mapper at each crossing. Adding a nullable field to a customer requires touching nine files, and every engineer on the team can recite the dependency rule.

When should a monolith be split into services, and what would you do first?Monolith

A four-year-old monolith, one repository, one deployment, eighteen engineers on three teams. Deploys are blocked several times a week by unrelated failing tests. Leadership has asked for a microservices migration plan.