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