SOLID PrinciplesLiskov Substitution PrincipleHard⏱️ ~4 min

LSP in Interviews: Common Questions and Patterns

Interviewers test LSP understanding through design challenges, code reviews, and scenario discussions. Here are the most common patterns and how to approach them.

Question Pattern 1: Identify LSP Violations

Typical Question: "Review this code. Does it violate any SOLID principles? How would you fix it?"
class Bird {
fly() { /* flying logic */ }
eat() { /* eating logic */ }
}

class Sparrow extends Bird {
// Works fine
}

class Penguin extends Bird {
fly() { throw new Error("Penguins cannot fly") }
}

Answer approach:

  • First, identify the violation: Penguin cannot be substituted for Bird because calling fly() breaks client expectations.
  • Second, explain the problem: Client code working with Bird assumes all birds can fly, but Penguin violates this assumption.
  • Third, propose solution: Separate FlyingBird and FlightlessBird interfaces, or use composition with a FlightCapability component that some birds have and others do not.

Question Pattern 2: Design from Scratch

Typical Question: "Design a parking lot system. How do you handle different vehicle types (Car, Motorcycle, Bus) and different spot sizes (Compact, Large, Handicapped)?"

LSP-compliant approach:

«interface»
Parkable
+ getVehicleSize(): VehicleSize
+ canFitInSpot(spot): boolean
△ (implements)
Car
+ canFitInSpot(spot)
Motorcycle
+ canFitInSpot(spot)
Bus
+ canFitInSpot(spot)

Key points to mention:

  • All vehicle types implement canFitInSpot() with consistent semantics: returns true if vehicle physically fits, false otherwise.
  • No vehicle type throws exceptions or returns unexpected values. A Bus checking a compact spot returns false, not an error.
  • Parking lot code can call vehicle.canFitInSpot(spot) for any Parkable without type checking.
  • If special logic is needed (handicapped permits), use composition: Vehicle has optional HandicappedPermit, not a subclass hierarchy.

Question Pattern 3: Refactoring Challenge

Typical Question: "We have a notification system with Email, SMS, and Push notifications. Some users have opted out of certain channels. How do you design this without LSP violations?"
Violates LSP
Notification.send() throws exception if channel is disabled. Client code must catch and handle.
LSP Compliant
Check isEnabled() before attempting send. All implementations return consistent Result type.
interface NotificationChannel {
isEnabled(user): boolean
send(user, message): Result
}

class NotificationService {
sendToUser(user, message, channels) {
for channel in channels {
if channel.isEnabled(user) {
result = channel.send(user, message)
// Handle result uniformly
}
}
}
}

Why this works: All channels implement send() with the same contract. Disabled channels are filtered before sending, not during. No channel throws unexpected exceptions or returns special error codes that others do not.

Machine Coding Considerations

Interview Tip: When coding, use guard clauses to validate LSP contracts at hierarchy boundaries. For example, assert that all vehicles return non-null size in getVehicleSize(). This shows you think about contracts, not just functionality.
  • First, design the interface or abstract class with clear contracts. Document preconditions and postconditions in comments.
  • Second, implement subclasses one at a time, verifying each honors the parent contract before moving to the next.
  • Third, write client code that uses the base type polymorphically. If you find yourself checking instanceof, you probably have an LSP violation.
  • Fourth, if a subclass cannot honor the contract, stop and refactor the hierarchy. Do not proceed with workarounds like throwing exceptions or returning null unexpectedly.

Red Flags Interviewers Look For

  • Type checking (if vehicle instanceof Bus) in polymorphic code
  • Empty method implementations or throwing UnsupportedOperationException
  • Methods returning null when parent promises non-null
  • Adding parameters to overridden methods (changes signature)
  • Documenting that certain subclasses "should not be used" in specific contexts

How to Articulate Your Reasoning

Use this template when discussing LSP in interviews:

"I am choosing [inheritance/composition] because [reason]. This maintains LSP by ensuring that [specific contract] is honored. If we used [alternative approach], we would violate LSP because [specific reason]. The client code can now [describe polymorphic usage] without type checking, which makes the design extensible for future [vehicle types/notification channels/etc.]."
💡 Key Takeaways
Identify LSP violations by looking for type checks, exceptions in overridden methods, or empty implementations
Design from scratch by defining clear interface contracts before implementing subclasses
Refactor violations by separating concerns into multiple interfaces or using composition
In machine coding, validate contracts and avoid instanceof checks in polymorphic code
Articulate trade-offs explicitly: explain why inheritance is appropriate or why composition is better
📌 Examples
1Bird-Penguin hierarchy with fly() method that throws exception
2Parking lot with vehicle types implementing consistent canFitInSpot() contract
3Notification system checking isEnabled() before send() to avoid exceptions
← Back to Liskov Substitution Principle Overview