Liskov Substitution Software Principle
The Liskov Substitution Principle says that any subclass inheriting from a parent class must be able to use everything the parent class offers. In other words, a class should never inherit a feature it has no real use for.
"Functions that use pointers or references to a base class must be able to use objects of derived classes without knowing it." — Robert C. Martin (Uncle Bob)
This principle is actually quite similar to the Open/Closed Principle — you could call it a special case of it. Both are about structures that stay open to extension.

The Liskov Substitution Principle is also closely tied to Polymorphism, one of the core OOP principles. Let's work through an example to really understand it — we'll keep building on the example from the previous principle.
Here's the setup: we define an Interface. Our other classes will inherit from it, unlocking the phone with an OpenLock method and adding a biometric passcode with an AddTouchId() method.

Then we create our iPhone, Samsung, and Xiaomi concrete classes, inheriting from the IMobilePhone interface. We're all set — now we can unlock every phone and add TouchId biometric locking to each one.

Job done — now let's add one more phone to the mix. Time to bring the legendary indestructible Nokia 3310 into this. Uh-oh — Nokia doesn't even have a TouchId feature! But our IMobilePhone interface forces every implementer to have one. For Nokia, the AddTouchId() method is completely pointless. So what do we do now?

We scramble and slap a special condition on Nokia. But are we really going to go through life writing if/else for every edge case like this? This obviously violates LSP — and since it does, it violates OCP too. Worst case, we just throw a NotImplementedException and catch it in a try/catch block, right? 😅
Let's actually solve this properly, by applying the Liskov Substitution Principle.

iPhone, Samsung, and Xiaomi all support both unlocking and biometrics, so they inherit from two interfaces. Nokia can only unlock, so it inherits only from ILockable. That way, it's never forced to carry an AddTouchId() method it will never use.
As you can see, we've split our base object up into interfaces. This is exactly the approach behind the Interface Segregation Principle.
Note: interfaces usually take an "I" prefix and an "-able" suffix.

And that's it! What did we actually do?
- We made our code a bit more generic and removed the dependency on conditionals (if/else).
- We ended up with a design that follows the Open/Closed Principle, because it's about extending, not modifying.
- Every structure does exactly one job, so we ended up with a design that follows the Single Responsibility Principle.
- We used interfaces to separate features, giving us a design that follows the Interface Segregation Principle.
SUMMARY
LSP is an incredibly useful idea to keep in mind, whether you're building a brand new application or extending and modifying an existing one.
When you're designing a class hierarchy for a new application, LSP helps you organize the concepts in your problem domain and write code that holds up over time.
Grateful that you read this.
Take care, friends...
To keep going with the SOLID principles, check out the Interface Segregation Principle:
👉 Interface Segregation Principle (ISP) — SOLID
References
What is Liskov Substitution Principle (Important)
Liskov Substitution With C# (Special Thanks)
LSP Abstraction Examples (Special Thanks)

