Complex version:
Subtype Requirement: Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.
Simple version:
Objects inheriting from the same object/interface should be interchangeable without altering the program correctness
The substitution principle basically says that objects with a common interface should respect that interface in terms of methods and behaviour.
When this principle is violated we create a leaky abstraction, which forces the user to discriminate based on the subtype or underlying implementation (e.g. Rectangle → Square…)
In Clean Architecture (book), Uncle Bob reminds us that this principle can be used also at the architectural level, and for things that are not code.
E.g. API design: if we design a common API that vendors need to implement, and one of them makes it slightly different, we will have to implement a more complex logic to use them