Low-Level Design (Object-Oriented Design) Module 4
Design principles: SOLID and beyond interview questions
Interview questions on “Design principles: SOLID and beyond”: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What does the Interface Segregation Principle say, and how do you spot a violation?
Answered in the lesson Interface Segregation Principle (ISP)
-
Fresher to mid
What is the difference between dependency inversion and dependency injection?
-
Fresher to mid
What does DRY mean, exactly, and what does it not mean?
-
Fresher to mid
What is the Law of Demeter, and what does a violation look like?
Answered in the lesson Law of Demeter, tell-don't-ask and CQS
-
Fresher to mid
What is GRASP, and what does Information Expert say?
-
Mid to senior
What do you do when two design principles point in different directions?
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
Your
FileStoreinterface has 12 methods, fromupload()tomake_thumbnail()andbill_usage(). Every new storage backend takes a week to write, and half of each backend's methods raiseNotImplementedError. How would you redesign the interface without breaking the 40 places in the code that use it?- What it tests
- Splitting an interface by its callers rather than its implementers, migrating call sites in small safe steps, and placing a capability that only some backends have.
- First step
- Build the caller matrix before changing any type: for each of the 40 call sites, list which of the 12 methods it uses, with a search or a short script over the source. Groups of call sites that use the same methods are your role interfaces, and a method that no call site uses is a candidate for deletion, not for a role.
- Follow-ups
-
- How do you migrate 40 call sites without one big change?
- Only one backend can make thumbnails. Where does that capability live now?
- How do you stop the old wide interface from growing back?
From the lesson Interface Segregation Principle (ISP)
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.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
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 3 minutes
-
Fresher to mid
A teammate split a six-method
Cartinterface into six one-method interfaces, one per method, to follow the Interface Segregation Principle. Is the new design better? Argue for or against it, and name the cost that each design creates.- What it tests
- Knowing that the principle groups methods by the callers that use them together, and weighing cohesion against segregation instead of counting methods.
- First step
- Ask who calls each method. If the checkout page uses add, remove, items and total together, a nightly job uses only export and the admin page uses only clear, then the interfaces should follow those three kinds of caller, not the six methods.
- Follow-ups
-
- When is a one-method interface exactly right?
- Does Python's typing.Protocol change the cost of having many small interfaces?
From the lesson Interface Segregation Principle (ISP)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Your
InvoiceServicecallsdatetime.now(),uuid4()and a tax-rate web API directly. Its tests fail around midnight and on the last day of each month, and they need the network. How do you make it testable without a dependency injection framework?- What it tests
- Finding hidden dependencies on time, randomness and the network, inverting them behind interfaces the service owns, and wiring them in one composition root.
- First step
- List everything the service reads that does not arrive in its arguments: the clock, the id generator and the tax API. Each one becomes a constructor parameter typed by a small interface named in the service's own terms, such as
Clock.now(),new_invoice_id()andTaxRates.rate_for(region, category). - Follow-ups
-
- Which tests would you write for the month-end failures once the clock is injected?
- The tax API is slow and sometimes down. Where do caching and retries go?
- How do you migrate the 15 callers that construct InvoiceService() with no arguments?
From the lesson Dependency Inversion Principle (DIP)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A teammate puts an interface in front of every class "for DIP":
IUserService,IUserRepository,IDateFormatterandIMoney, each with exactly one implementation. Where is the inversion worth its cost, and where is it noise? Give a rule you would defend in a design discussion.- What it tests
- Knowing that the principle protects policy from volatile details, judging which boundaries change or need fakes, and counting what an interface with one implementation costs.
- First step
- Sort the classes by what they touch. A repository talks to a database and a gateway to the network: those details change and tests need fakes for them, so the policy should own an interface for each. A date formatter and a Money value object are stable and have no side effects, so callers can use them directly.
- Follow-ups
-
- IUserService has one implementation and no test replaces it. Keep it or delete it?
- Where should the repository interface live, with the service or with the database code?
- What would make you add an interface later, and how expensive is that change?
From the lesson Dependency Inversion Principle (DIP)
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Six months ago a colleague merged two near-identical 40-line functions, for domestic and international shipping fees, into one
shipping_fee()with three boolean flags. Now almost every change for one kind of shipment breaks the other. What do you do, step by step, and how do you stop it happening again?- What it tests
- Recognising the wrong abstraction, undoing it safely under tests, and telling shared knowledge from code that only looks the same.
- First step
- Pin down today's behaviour first: write characterisation tests for every combination of the three flags that callers really pass, which a search of the call sites shows. Then copy
shipping_fee()back into each kind of caller, delete the branches each copy never takes, and only then look for the facts that both rules truly share. - Follow-ups
-
- How many flag combinations do you need to test, and how do you find out?
- After the split, both functions use the same weight bands. Do you share them?
- What would you write into the team's guidelines?
From the lesson DRY, KISS and YAGNI without dogma
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Before the first discount rule shipped, your team spent three weeks building a plugin framework for discount rules. A year later there are two rules, and each change to them takes about twice as long as it should. How do you judge whether that work paid off, and how would you decide next time?
- What it tests
- Weighing a speculative design with the costs of build, delay, carry and repair, using numbers, and knowing what keeps a later change cheap.
- First step
- Put numbers on the four costs: three weeks of building, the features those weeks pushed back, the extra time each of the year's changes took, and whether the framework fitted the rules that actually arrived. Compare the total with what turning a plain function into a seam would have cost when the second rule came.
- Follow-ups
-
- What would have justified building the framework up front?
- Do you remove the framework now, and how?
- What do you build the next time someone predicts many rules?
From the lesson DRY, KISS and YAGNI without dogma
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A checkout function computes
cart.customer.membership.discount_rate()andcart.customer.address.region.tax_rate(). The business now lets a company account pay for its employees' orders, with its own discount and billing address. What breaks, and how would you redesign the checkout so that the next change like this touches one class?- What it tests
- Seeing the structural knowledge a chain carries, moving pricing decisions to the objects that own the data, and naming where a new concept such as the payer belongs.
- First step
- Write down what the checkout knows: that a cart has one customer, that the customer holds a membership and an address, and that an address has a region. The new requirement changes exactly these facts, because the payer is no longer always the customer, so every chain that starts at
cart.customeris now wrong. - Follow-ups
-
- Where does the new payer concept live?
- How many fakes does a checkout test need before and after the redesign?
- When is a chain like this acceptable?
From the lesson Law of Demeter, tell-don't-ask and CQS
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A
charge_and_get_receipt(order)method charges a card and returns the receipt. When the payment provider times out, the client retries, and some customers are charged twice. How does command-query separation help here, and what else does the design need?- What it tests
- Separating commands from queries, making a retried command idempotent, and finding out the result of a command that timed out.
- First step
- Split the method into a command that charges and a query that returns the receipt or the charge's status. Then give each charge an idempotency key that the client chooses before the first attempt, so that a retried command with the same key is recognised and not charged again.
- Follow-ups
-
- The provider timed out. Was the card charged or not?
- Is returning a payment id from a charge command a violation of the rule?
- Where do the retry rules live?
From the lesson Law of Demeter, tell-don't-ask and CQS
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
In a machine-coding round your
ParkingServicehas grown to 600 lines: it computes fees, picks free spots, prints tickets and calls the payment API. Using GRASP, how would you redistribute these responsibilities, and how would you justify each move in one sentence?- What it tests
- Applying Information Expert, Creator, Pure Fabrication and Protected Variations to an overgrown controller, and keeping the controller thin.
- First step
- List what the service knows and does, then ask for each item which class has the data. The fee depends on the ticket's entry time and the vehicle type, a free spot on the floors and their spots, the printed text on nothing in the domain, and the payment on an outside provider.
- Follow-ups
-
- Who creates the Ticket?
- What stays in ParkingService?
- Where does the fee rule go when weekends cost more?
From the lesson GRASP: assigning responsibilities
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Fresher to mid
A candidate argues that
Ordershould havesave()andto_pdf()methods, because by Information Expert it has all the data they need. Do you agree? Answer with GRASP, and say where those two responsibilities should go.- What it tests
- Knowing when High Cohesion and Low Coupling overrule Information Expert, and using Pure Fabrication for persistence and formatting.
- First step
- Ask what each method would make
Orderdepend on, and why it would change.save()ties the order to a database driver and its tables;to_pdf()ties it to a PDF library and a page layout. Neither of those reasons to change belongs to orders. - Follow-ups
-
- Is a Pure Fabrication just a utility class?
- When is order.total() still the right call?
- How would you test the code that saves orders?
From the lesson GRASP: assigning responsibilities
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Fresher to mid
With 30 minutes left in a machine-coding round, your parking fee is one function with four branches, and the interviewer asks: "Shouldn't this follow the Open/Closed Principle?" How do you decide whether to refactor now, and what do you say?
- What it tests
- Weighing a seam against the time left and the stated requirements, and explaining a trade-off clearly under pressure.
- First step
- Recall what the requirements say will change: new vehicle types, new rules for every type, or neither. Then estimate both paths in minutes: the refactoring and its tests now, against the one edit a new type would need later, and say which force wins with the time that is left.
- Follow-ups
-
- The interviewer then adds a fifth vehicle type. What do you do?
- What do you say if the interviewer insists on the refactoring?
- Which tests make the later refactoring safe?
From the lesson When design principles conflict
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
All Low-Level Design (Object-Oriented Design) interview questions · The Low-Level Design (Object-Oriented Design) track