In this article
  1. Principles and vocabulary
  2. Design patterns
  3. Classic design problems
  4. Making a design hold up
  5. Follow-up questions
  6. Sources

Principles and vocabulary

What is low-level design, and how does it differ from high-level design?

Low-level design is the design of the classes, interfaces and interactions inside one module; high-level design covers the components of a whole system and how they connect. Robert Martin calls architecture multitiered: architecture patterns set an application's overall shape, the next level is specific to the application's purpose, and below that sits the architecture of modules and their interconnections, the domain of design patterns, packages, components and classes (Martin, 2000). Low-level design interviews live at that last level.

Why it is asked: it checks that you know which level the interviewer wants. Read the full explanation: machine coding rounds guide.

What is the Single Responsibility Principle?

The Single Responsibility Principle says each software module should have one and only one reason to change. Martin restates it as gathering together the things that change for the same reasons and separating those that change for different reasons. He adds that the reasons for change are people, so code that different groups care about should not be mixed in one class (Martin, 2014).

Why it is asked: it is the quickest test of whether your classes have sensible boundaries. Read the full explanation: machine coding rounds guide.

What is the Open Closed Principle?

The Open Closed Principle says a module should be open for extension but closed for modification. Martin credits Bertrand Meyer and says it means you can change what a module does without changing its source code, and that abstraction is the key. His example is a LogOn function that must be edited every time a new kind of modem is added, which dynamic polymorphism avoids (Martin, 2000).

Why it is asked: a new requirement should add a class, not edit a switch statement. Read the full explanation: Java object-oriented interview questions.

What is the Liskov Substitution Principle?

The Liskov Substitution Principle says subclasses should be substitutable for their base classes: code that uses a base class should keep working when handed a derived class. Martin notes that Barbara Liskov coined it in her work on data abstraction and type theory, and that it also derives from Bertrand Meyer's Design by Contract (Martin, 2000). A subclass that throws on a method its parent supports breaks it.

Why it is asked: it exposes misuse of inheritance. Read the full explanation: Java object-oriented interview questions.

What do the interface segregation and dependency inversion principles say?

Interface segregation says many client-specific interfaces are better than one general-purpose interface. Dependency inversion says depend on abstractions, not on concretions. Martin writes that the Open Closed Principle states the goal of object-oriented architecture and dependency inversion states the primary mechanism: high-level policy depends on an interface, and the detail implements it (Martin, 2000).

Why it is asked: both show whether you can keep a class easy to replace. Read the full explanation: Java object-oriented interview questions.

What is dependency injection, and why does it help testing?

Dependency injection is giving a class the objects it needs instead of letting it create them. Martin Fowler describes the idea as a separate object, an assembler, that populates a field in the class with an implementation of an interface, and names three styles: constructor, setter and interface injection (Fowler, 2004). A test can then pass a simple fake in place of a real service.

Why it is asked: injectable design is what makes a class testable. Read the full explanation: machine coding rounds guide.

Abstract class or interface: how do you choose?

Choose an interface when unrelated classes share one capability, and an abstract class when closely related classes share code or state. Oracle's Java tutorial gives the mechanics: an abstract class can declare fields that are not static and final, interface fields are always public, static and final, and a class extends only one class but can implement any number of interfaces (Oracle Java tutorial). Python's abc module provides abstract base classes, as outlined in Python Enhancement Proposal (PEP) 3119 (Python abc documentation).

Why it is asked: it checks whether you model roles before implementations. Read the full explanation: Java object-oriented interview questions.

Design patterns

What are the categories of design patterns?

A design pattern is a named, reusable solution to a recurring design problem. Refactoring.Guru's catalog groups patterns into three kinds: creational patterns provide object creation mechanisms, structural patterns explain how to assemble objects into larger structures, and behavioral patterns concern algorithms and the assignment of responsibilities between objects. The catalog lists five, seven and ten patterns respectively (Refactoring.Guru catalog).

Why it is asked: knowing the categories helps you name the pattern you are using. Read the full explanation: Java object-oriented interview questions.

When would you use the Strategy pattern?

Use Strategy when one task has several interchangeable algorithms and the caller should not care which runs. Refactoring.Guru defines it as a behavioral pattern that lets you define a family of algorithms, put each in a separate class, and make their objects interchangeable. Its example is a navigation app whose route planning must support more than roads, so each travel mode becomes its own strategy (Refactoring.Guru, Strategy).

Why it is asked: pricing, fee and routing rules are favorite interview material. Read the full explanation: Java object-oriented interview questions.

When would you use the Observer pattern?

Use Observer when many objects need to learn about events in one object without that object knowing who they are. Refactoring.Guru defines it as a subscription mechanism to notify multiple objects about events that happen to the object they observe. Its example is customers waiting for a product in a store: visiting daily wastes trips, and emailing everyone upsets those uninterested (Refactoring.Guru, Observer).

Why it is asked: notifications and event handling appear in many designs. Read the full explanation: Java object-oriented interview questions.

When would you use the State pattern?

Use State when an object behaves differently depending on its internal state and the state changes over time. Refactoring.Guru defines it as a behavioral pattern that lets an object alter its behavior when its internal state changes, and relates it to a finite-state machine: a finite number of states, with different behavior in each (Refactoring.Guru, State). A vending machine or an elevator controller is a typical fit.

Why it is asked: it replaces nested conditionals with named states. Read the full explanation: Java object-oriented interview questions.

Is the Singleton pattern a good idea?

Use it rarely. Singleton ensures a class has only one instance and gives a global access point to it. Refactoring.Guru lists its drawbacks: it solves two problems at once and so violates the Single Responsibility Principle, it can mask bad design, it needs special treatment in multithreaded code so threads do not create several instances, and it can make client code hard to unit test (Refactoring.Guru, Singleton).

Why it is asked: a strong answer names the cost of the pattern. Read the full explanation: Java object-oriented interview questions.

What is the Composite pattern, and when does it fit?

A composite is a structure in which a group of objects is treated like a single object. Refactoring.Guru defines the Composite pattern as one that lets you compose objects into tree structures and work with them as if they were individual objects, and says it makes sense only when the core model can be represented as a tree. You declare a component interface, leaf classes and container classes (Refactoring.Guru, Composite).

Why it is asked: folders, menus and task trees all need it. Read the full explanation: Java object-oriented interview questions.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Classic design problems

How do you design an LRU cache class?

A least recently used (LRU) cache is a fixed-size store that evicts the entry unused for the longest time when it is full. Combine a hash map for lookup with an order of use. Python's OrderedDict has move_to_end and popitem(last=False), and its documentation says it suits LRU caches (Python collections documentation). Java's LinkedHashMap has an access-order mode and an overridable removeEldestEntry (Java 21 documentation). On an illustrative 12-request trace with room for three entries, LRU eviction gave 4 hits and first-in-first-out (FIFO) eviction gave 3.1

Why it is asked: it combines a data structure choice with a clean class interface. Read the full explanation: LRU cache implementation in Python.

LRU eviction kept one more hit than FIFO on this traceBar chart of cache hits on an illustrative 12-request trace with room for three entries: least-recently-used eviction scores 4 hits and first-in-first-out eviction scores 3 hits.Cache hits out of 12 (hits)0246LRUFIFOEviction rule43Diagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/low-level-design-interview-questions.html
Evicting the least recently used entry kept one more hot key than evicting the oldest entry on this trace.CC BY 4.0 — source: Low level design interview questions on principles, patterns and thread safety
Text description of the diagram
  • LRU eviction gave 4 hits.
  • FIFO eviction gave 3 hits on the same 12 requests.

How do you design a URL shortener class?

Give the class two methods, shorten(long_url) returning a code and resolve(code) returning the long address, backed by a map. Generate codes from a counter encoded in base 62: letters and digits, all unreserved characters in Request for Comments (RFC) 3986 (RFC 3986). Six characters give 56,800,235,584 codes and seven give 3,521,614,606,208.1 Counter codes never repeat, but a custom alias can claim a code the counter reaches later, so check every new code against the map and decide what a repeated long address returns. Write a failing round-trip test first.

Why it is asked: it tests encoding, uniqueness and testable design in one class. Read the full explanation: web address and resource design questions.

How do you design a rate limiter class?

Model a token bucket: the class holds a capacity and a refill rate, and allow() tops up the tokens for the time elapsed, never above the capacity, then spends one if available. Measure elapsed time with a monotonic clock, which Python's documentation describes as one that cannot go backwards and is not affected by system clock updates (Python time documentation). Guard the state with a lock if several threads call it.

Why it is asked: it tests time handling and concurrency in a small class. Read the full explanation: designing a distributed rate limiter.

How would you build a task manager where a task can contain smaller tasks?

Use the Composite pattern: a task and a task list share one interface, and a task holds child tasks. A parent reports itself complete when all its children are, and operations such as progress, count or delete recurse over the children. Guard against cycles by refusing to add a task under its own descendant. This follows the tree shape that Refactoring.Guru says the pattern needs (Refactoring.Guru, Composite).

Why it is asked: it checks whether you recognize a tree in the requirements. Read the full explanation: nested list traversal.

How do you model an organization to find direct reports and everyone under a department?

Store each employee with a manager identifier and keep a map from each manager to the list of direct reports, so direct reports are a single lookup. Everyone under a department or manager is a traversal of the tree below that node, whose cost grows with the size of the subtree, not the company. Departments can be a separate type that points at a head and a list of units.

Why it is asked: it tests choosing a data structure for the query you need. Read the full explanation: nested list traversal.

How do you design a parking lot?

Start by asking about vehicle types, levels, pricing and payment, then turn the nouns into classes: lot, level, spot, vehicle and ticket. Put rules that vary, such as how a spot is chosen and how a fee is computed, behind interfaces so they can be swapped, which is the Open Closed Principle in practice. Each class should have one reason to change (Martin, 2014).

Why it is asked: it is the standard open-ended prompt for requirements and decomposition. Read the full explanation: machine coding rounds guide.

Making a design hold up

How do you make a class thread-safe?

Protect every read and write of shared state with the same lock, and keep the locked section small. Python's documentation describes a primitive lock as having two states, locked and unlocked, with acquire() and release() as its basic methods, and says Lock objects may be used in a with statement so the lock is always released (Python threading documentation). Prefer immutable objects where possible.

Why it is asked: designs that work for one thread often break under two. Read the full explanation: concurrency interview questions.

For where these rounds sit in a hiring process, see the interview loops guide and the system design guide.

Follow-up questions

How would you make your LRU cache safe when two threads use it?

Put one lock around the whole of every get and put, because a get also moves the entry in the order of use, so even a read changes shared state. Keep the locked section to the map and order updates, and test with several threads on the same keys.

How would you add an expiry time to each cache entry?

Store an expiry time read from a monotonic clock with each entry, treat an expired entry as a miss when it is read, and remove it then. Pass the clock in as a dependency so a test can move time forward without sleeping.

What changes in your parking lot design if the lot adds charging spots for electric cars?

If spot choice already sits behind an interface, you add a spot type and a new selection strategy without editing the existing ones. If you have to edit a switch statement instead, the design was not open for extension.

Key takeaways
  • Low-level design works at the level of classes and interfaces inside a module; give each class one reason to change.
  • Extend by adding classes, not editing switches, and depend on abstractions that tests can replace.
  • Name the pattern and its cost: Strategy and State for varying behavior, Observer for events, Composite for trees, Singleton rarely.
  • For an LRU cache, pair a hash map with an order of use; for a rate limiter, use a monotonic clock and a lock.
  • Clarify requirements first, then decompose nouns into classes.

Sources

  1. Design Principles and Design Patterns (Robert C. Martin) — Robert C. Martin, Object Mentor (copyright 2000), 1 Jan 2000
  2. The Single Responsibility Principle (Clean Coder Blog) — Robert C. Martin, 8 May 2014
  3. Inversion of Control Containers and the Dependency Injection pattern — Martin Fowler, 23 Jan 2004
  4. The Catalog of Design Patterns — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  5. Composite (Refactoring.Guru) — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  6. Strategy (Refactoring.Guru) — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  7. Observer (Refactoring.Guru) — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  8. State (Refactoring.Guru) — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  9. Singleton (Refactoring.Guru) — Refactoring.Guru (undated page, read 2026-10-24), 24 Oct 2026
  10. collections, OrderedDict objects (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  11. LinkedHashMap (Java SE 21 API documentation) — Oracle (read 2026-10-24), 24 Oct 2026
  12. RFC 3986: Uniform Resource Identifier (URI) Generic Syntax — IETF (Berners-Lee, Fielding, Masinter; January 2005), 1 Jan 2005
  13. time, monotonic (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  14. threading, Lock objects (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  15. Abstract Methods and Classes (The Java Tutorials) — Oracle (read 2026-10-24), 24 Oct 2026
  16. abc, Abstract Base Classes (Python 3.15 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026