If you were an infantryman in the 14th century, charging up a hill with your rifle (they weren’t really called rifles yet, but let’s not quibble), you’d not only have to deal with the mud and the thunder of the battlefield, but the difficult task of reloading your rifle. Powder. Padding. Bullet. Wadding. Priming. Worst of all, eventually you’d put your hand into your bullet pouch, and find it empty.
Once that happened, you were done shooting for the day. You couldn’t borrow a bullet from the guy next to you. It either wouldn’t fit, or it would cause your gun to explode. You see, each and every rifle in use by your army had been made by hand, and no two barrels were the same. You had to find a bullet that would fit, and that took some time. Eventually with bore width for gun barrels was standardized, even centuries before Honore Blanc demonstrated the first true interchangeable parts to Thomas Jefferson. Today, we take it as a given that we can use similar parts interchangeably without risk, but that’s due to centuries of effort to make sure that our common assumptions are followed by everyone.
In software, the Liskov Substitution Principle is meant to bring the same benefits to our code. It states that Methods that use references to base classes must be able to use objects of derived classes without knowing it. This is a simple concept, but it is easy to accidentally violate the principle if you don’t give sufficient thought to your derived classes.
An obvious failing is to not use virtual/override keywords on your methods. This would mean your subclass code never gets called when someone is using a reference to the base class. A more significant issue is when the subclass changes behavior in a way that violates the assumptions of the base class. “Square” could be a subclass of “Rectangle”, but when you change the height of a square, the width changes automatically. This is not true of a rectangle. Unintended side effects can break your code at runtime, often unpredictably. Following the Liskov Substitution Principle means you can avoid some problems. (If you want to research more, the Uncle Bob article is great, and “Design By Contract” is a similar concept with a lot of coverage.)
Commonly we use the language feature of inheritance for things that it was not designed for. If you need to re-use code, there are other ways of including it than putting it in a base class, composition is frequently better than inheritance. Similarly, polymorphism can be made more granular by implementing interfaces, rather than having a over-broad base class that may or may not be fully implemented by its inheritors.