Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Low-Level Design (Object-Oriented Design) Module 9

One design, six languages interview questions

Interview questions on one design, six languages: short warm-ups, then what each harder question tests, the first step of a good answer and its follow-ups.

23 questions: 6 warm-ups and 17 with model answers · Levels: Fresher to staff

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher

    When should a type in a low-level design be a Java record, and when a class?

    Answered in the lesson LLD in Java 25: records, sealed types, patterns

  2. Fresher to mid

    What does frozen=True give a Python dataclass, and what does it not give it?

    Answered in the lesson LLD in Python 3.14: dataclasses and protocols

  3. Fresher to mid

    What is the difference between TypeScript's private modifier and a JavaScript #private field?

    TypeScript's private is checked only by the type checker. Once the types are stripped, as Node.js does when it runs a .ts file, the field is an ordinary property: it appears in JSON.stringify and Object.keys, and any code can read it with a cast or bracket notation. A #private field is part of JavaScript itself, so the engine enforces it at run time: no outside code can name it, it never appears in JSON or in the object's keys, and a method that reads it from an object its class did not create throws a TypeError. Use #private for state that must stay hidden, such as tokens or amounts that only methods may change; private hides nothing at run time.

    From the lesson LLD in TypeScript with erasable syntax only

  4. Fresher to mid

    How does a Go type satisfy an interface, and where should the interface be declared?

    Implicitly: a type satisfies an interface when it has all of the interface's methods with the right signatures, and it never names the interface. Because of that, Go declares an interface in the package that uses it, with only the methods that package calls, and the implementing package returns concrete types. A billing package that needs a fee declares a one-method FeePolicy, and any struct with that method fits, including test doubles. Small interfaces keep fakes small and let types written elsewhere satisfy them. The compiler checks a fit only where a value is assigned to the interface, so a design adds an explicit check next to each implementation.

    From the lesson LLD in Go: interfaces, embedding, generics

  5. Fresher to mid

    What is the rule of zero in C++, and why does it matter in a low-level design?

    The rule of zero says that a class should not declare a destructor, copy or move functions of its own when its members can manage their resources themselves. Hold parts by value, text in std::string, collections in standard containers and a sole heap object in std::unique_ptr, and the compiler generates correct copying, moving and destruction from the members. In a design this keeps ownership visible in the member types instead of hidden in hand-written functions, which are where double frees and leaks come from. When a class must manage a resource directly, the C++ Core Guidelines ask for all of those functions to be defined or deleted together.

    From the lesson LLD in modern C++: ownership and variants

  6. Fresher to mid

    Rust has no inheritance. How does a Rust design share behaviour and model "is a kind of"?

    Behaviour is shared through traits: a trait declares methods, may provide default implementations, and any type can implement it, including types from other crates for your own traits. Code that needs the behaviour takes a generic parameter bounded by the trait, or a trait object such as Box<dyn Trait> when the concrete type is chosen at run time. Data is shared by composition: a struct holds other structs as fields. A closed family of kinds, such as a door's states or an operation's outcomes, is an enum whose variants carry their own data, handled with a match that the compiler checks for completeness.

    From the lesson LLD in Rust: traits, enums and ownership

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Mid to senior

    Payment states are records that implement a sealed interface. A teammate adds PartlyRefunded, and the build now fails in nine switch expressions. Another engineer suggests adding default branches so that it compiles. What do you reply, and where, if anywhere, does a default branch belong?

    What it tests
    Reading exhaustiveness errors as a list of decisions, the cost of default branches over sealed types, and what code compiled against the old hierarchy does when it meets the new state.
    First step
    Treat the nine errors as the list of decisions the change needs: each switch answers a question, such as how much a payment settles or what a customer is told, and partial refunds change the answer. Go through them one by one and keep defaults out, because a default answers all nine silently with the old behaviour.
    Follow-ups

    From the lesson LLD in Java 25: records, sealed types, patterns

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  2. Fresher to mid

    Design the value types of a wallet in Java 25: an amount of money, a transfer between two wallets and a monthly statement that lists transfers. Which of them are records, what does each constructor check, and how do you keep the statement immutable?

    What it tests
    Choosing records for values, checks in compact constructors, money in minor units, defensive copies of collection components, and equality that stays stable.
    First step
    Start with Money: a record holding a whole number of paise in a long, never a double, whose compact constructor refuses negative amounts. A Transfer is a record of two wallet ids and a Money that refuses a zero amount and a transfer from a wallet to itself.
    Follow-ups

    From the lesson LLD in Java 25: records, sealed types, patterns

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Mid to senior

    Three teams will write payment-gateway adapters for your Python checkout service. Do you define the gateway as an abstract base class or as a Protocol, and how do you make sure every adapter really fits before it reaches production?

    What it tests
    Nominal against structural typing, which check runs at which moment, and turning a contract into tests that every implementation must pass in continuous integration.
    First step
    Separate what the adapters must share from what must be checked. Shared retry or logging code argues for a base class; adapters that should not import your package, or vendor classes you cannot change, argue for a Protocol. Then name where each check runs: at instantiation, in a type checker, or in a contract test.
    Follow-ups

    From the lesson LLD in Python 3.14: dataclasses and protocols

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  4. Fresher to mid

    Order is a frozen dataclass, yet support reports that a paid order's total changed after the customer edited their cart. How can that happen, and how would you redesign Order so that it cannot?

    What it tests
    Shallow immutability, defensive copies at construction, and telling a value from an entity with a lifecycle.
    First step
    Ask what the order's lines field holds. If it is the list the cart passed in, frozen=True stops reassigning the field but not appending to the list. Reproduce it with a test that edits the cart after the order exists, and watch the total move.
    Follow-ups

    From the lesson LLD in Python 3.14: dataclasses and protocols

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  5. Mid to senior

    Your team moves a Python 3.12 code base to 3.14, and someone opens a change that removes the quotes from every forward reference in annotations. What can break, and how do you roll the change out safely?

    What it tests
    Deferred evaluation of annotations, code and libraries that read annotations at run time, and a migration order that keeps the old version working.
    First step
    Separate code that only writes annotations from code that reads them while the program runs: dataclasses, serialisers, validation and dependency-injection libraries, and your own metaclasses. Find every reader first, and run the whole test suite on 3.14 before any quote is removed.
    Follow-ups

    From the lesson LLD in Python 3.14: dataclasses and protocols

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  6. Mid to senior

    A TypeScript service has 40 enums and parameter properties in most classes, and the team wants to run it directly on Node.js 24 without a build step. How do you plan the migration, and what replaces each construct?

    What it tests
    Knowing which syntax is erasable, keeping runtime behaviour identical while the syntax changes, and using the compiler to find every place that needs work.
    First step
    Turn on erasableSyntaxOnly and verbatimModuleSyntax in tsconfig while the old build still runs, so tsc lists every construct to replace as TS1294 or TS1484 errors. Then sort them: enums, parameter properties, namespaces and import aliases each have one mechanical rewrite.
    Follow-ups

    From the lesson LLD in TypeScript with erasable syntax only

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  7. Fresher to mid

    In a TypeScript booking service, a ShowId was passed where a SeatId was expected, and two customers were given the same seat. How do you design the identifiers so that this cannot compile, and where do you check them at run time?

    What it tests
    Structural typing and its limits, branded or nominal-style types, and validating untyped input once at the boundary.
    First step
    Explain why it compiled: both IDs are strings, and structural typing accepts any value of the right shape. Give each ID a brand that exists only in the type, and allow only a checking constructor function to create one.
    Follow-ups

    From the lesson LLD in TypeScript with erasable syntax only

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  8. Mid to senior

    A checkout holds seats, charges a card and then confirms. Every hold must end, whether the charge succeeds, is declined or throws. How do you design this in TypeScript on Node.js 24, and what can still leave a seat held?

    What it tests
    Deterministic cleanup with using and Symbol.dispose, the order of release, and failures that no in-process mechanism survives.
    First step
    Make each hold an object whose [Symbol.dispose]() releases it, and acquire holds with using declarations inside the checkout function, so every exit releases them in reverse order. Then ask what happens when the process dies between the charge and the confirmation.
    Follow-ups

    From the lesson LLD in TypeScript with erasable syntax only

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  9. Mid to senior

    A teammate ports a Java class hierarchy to Go: Account has Statement(), which calls Fee(), and PremiumAccount embeds Account and defines a lower Fee(). Premium customers are still charged the standard fee. Explain why, and redesign it.

    What it tests
    Method promotion and receivers in embedding, the absence of virtual dispatch, and composition with explicit strategies.
    First step
    Say what premium.Statement() really calls: it is premium.Account.Statement(), whose receiver is the inner Account, so its call to Fee() can only reach Account.Fee. Reproduce it with a test that compares the two statements.
    Follow-ups

    From the lesson LLD in Go: interfaces, embedding, generics

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Fresher to mid

    After a refactor, every successful seat reservation in a Go service logs "reservation failed: <nil>" and is rolled back. The function returns error. What is the likely bug, how do you prove it, and how do you fix and prevent it?

    What it tests
    The two-word representation of interface values, typed nil pointers stored in error, and guarding the pattern in reviews and tests.
    First step
    An error that prints as <nil> but compares unequal to nil is an interface holding a nil pointer. Look for a variable of a concrete error type, such as var err *ReservationError, that is returned on the success path.
    Follow-ups

    From the lesson LLD in Go: interfaces, embedding, generics

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    Your team adopts Go 1.27 and wants a generic Repository[K, V] with Map, Filter and GroupBy methods, plus a Repository interface that services depend on, so they can swap in a database later. How do you design it, and what can and cannot be generic?

    What it tests
    What Go 1.27 generic methods allow, why interface methods cannot have type parameters, and separating the storage contract from conveniences.
    First step
    Split the design in two: the contract that services depend on (Get, Put, Delete with concrete parameter types) and conveniences on the in-memory type (Map, Filter, GroupBy as generic methods). Then check each method against what an interface may contain.
    Follow-ups

    From the lesson LLD in Go: interfaces, embedding, generics

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  12. Mid to senior

    A notification service stores channels as Notifier* and deletes them through the base. Memory grows in production and some SMS batches are never sent, yet the sanitizer job on macOS reports nothing. What do you suspect, how do you confirm it, and how do you fix the design?

    What it tests
    Virtual destructors and undefined behaviour on delete through a base, the limits of sanitizers per platform, and ownership with unique_ptr.
    First step
    Check the base class first: if Notifier's destructor is not virtual, deleting an SmsNotifier through a Notifier* never runs SmsNotifier's destructor, so whatever it flushes or frees is skipped. Search the build logs for -Wdelete-non-abstract-non-virtual-dtor and -Wdelete-abstract-non-virtual-dtor warnings.
    Follow-ups

    From the lesson LLD in modern C++: ownership and variants

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  13. Mid to senior

    In a C++ parking design, spots have states (free, reserved, occupied, out of service) and payments have methods (card, wallet, cash, bank transfer, with more planned). Which do you model with std::variant and which with virtual functions, and why?

    What it tests
    Closed against open sets, which change each representation makes cheap, exhaustiveness checks, storage and ownership costs.
    First step
    Ask what changes more often for each: new states or new operations on states, new payment methods or new operations on payments. A closed set that gains operations suits a variant; an open set that gains kinds suits a virtual interface.
    Follow-ups

    From the lesson LLD in modern C++: ownership and variants

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  14. Senior to staff

    Two senior engineers disagree about a C++23 booking engine: one wants exceptions everywhere, the other std::expected everywhere. What do you decide, where does each mechanism belong, and how do you enforce the decision?

    What it tests
    Classifying failures, the costs and guarantees of exceptions and std::expected, and making a policy that tools and reviews can check.
    First step
    Classify the failures before choosing: refusals that callers handle as part of the flow (seat taken, over limit), faults of the environment (database down), and bugs (a broken invariant). Then give each class one mechanism and say why.
    Follow-ups

    From the lesson LLD in modern C++: ownership and variants

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  15. Mid to senior

    A developer ports a Java parking design to Rust: tickets hold references to spots, spots to their level, and levels back to the lot. The borrow checker rejects it at every turn. How would you redesign the object graph, and what does the new design cost?

    What it tests
    Ownership and borrowing in long-lived object graphs, IDs and newtypes, and when shared ownership with Rc and Weak is justified.
    First step
    Name the owner of each object: the lot owns levels and spots, and tickets belong to the lot too. Then replace every reference between them with an ID that the owner resolves, and see which links remain that are truly shared.
    Follow-ups

    From the lesson LLD in Rust: traits, enums and ownership

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  16. Mid to senior

    A pricing engine in Rust applies one of several discount strategies per order, chosen from configuration that can change while the service runs. Do you use generics or trait objects, and what happens when a teammate adds a generic method to the strategy trait?

    What it tests
    Static against dynamic dispatch, dyn compatibility, and keeping a trait usable as an object as it grows.
    First step
    Ask when the strategy's type is known. Chosen from configuration at run time, it must be a trait object such as Box<dyn Discount> or Arc<dyn Discount>; fixed per build or per call site, a generic parameter is simpler and faster.
    Follow-ups

    From the lesson LLD in Rust: traits, enums and ownership

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  17. Senior to staff

    A Rust service built its domain model from Rc<RefCell<T>> everywhere. In production it now panics with "RefCell already borrowed" when a spot change notifies an observer that reads the lot. How do you fix this incident, and how would you redesign the model?

    What it tests
    Runtime borrow checking, re-entrant calls through observers, and designs that keep borrowing rules at compile time.
    First step
    Find the borrow that is held across the call: the lot is borrowed mutably while it notifies observers, and an observer borrows the lot again. Reproduce it in a test with one observer that reads the lot during a notification.
    Follow-ups

    From the lesson LLD in Rust: traits, enums and ownership

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes

All Low-Level Design (Object-Oriented Design) interview questions · The Low-Level Design (Object-Oriented Design) track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.