The Liskov Substitution Principle (LSP) states that when you substitute a parent object reference with an object of any of its subclasses, the program should not break.
If a thing looks like a duck and it quacks like a duck, then it better be a duck.
Suppose that your duck has a baby duck. The baby duckling also looks like a duck and quacks like a duck and it also... breathes fire. Whoopsie. That's not supposed to happen.
In this quack analogy, the duck is the parent class. It exhibits duck-like attributes. These duck-like attributes are parts of the duck contract (a duck contract sets up a certain expectation of what a duck class should be like). The duckling is the subclass. Being a descendent of a duck, you expect it to also exhibit duck-like attributes. However, this fire-breathing ability violates the contract because ducks don't breathe fire. This is a violation of the Liskov Substitution Principle.
The LSP doesn't prevent the subclasses from adding new methods. A
babyDuck class can have differing attributes like
size = "small" and
color = "yellow" while the parent class has attributes
size = "medium" and
color = "brown". These differences won't violate the LSP because they fall within the user expectation. The subclasses don't need to return identical outputs as the parent, but they need to be consistent. A duck subclass with
fireBreathing = true attribute is not consistent because it is not the trait of a duck. It is a duck-dragon chimera.
At the heart of LSP, it's all about having consistent interfaces and following contracts.