On this page
Data abstraction allows you to use a complex system without knowing its inner workings. But when one software component replaces another, how do you know the system won't break? In 1987, Barbara Liskov solved this with a simple logical rule that became the foundation of reliable object-oriented software design.
What is data abstraction in programming?
Data abstraction separates what a program does from how it does it. Instead of manipulating raw data directly, a programmer interacts with an object through a defined set of operations or a "contract".
Liskov’s work in the 1970s and 1980s shifted the focus of computer science from writing isolated procedures to designing abstract data types. If a component relies only on the abstract interface, the underlying implementation can be upgraded or rewritten without breaking the rest of the system. For her "contributions to practical and theoretical foundations of programming language and system design, especially related to data abstraction, fault tolerance, and distributed computing," Liskov was awarded the 2008 A.M. Turing Award.
What is the Liskov substitution principle?
While data abstraction provides a contract, inheritance allows developers to create new types based on old ones. But how do you know if a new "subtype" is truly a safe replacement?
Liskov introduced the answer in her 1987 keynote address at the OOPSLA conference, titled "Data abstraction and hierarchy". She proposed that a subtype should be able to replace a supertype without altering any of the desirable properties of a program, such as correctness.
In 1994, Liskov and Jeannette Wing formalized this idea in their paper "A behavioral notion of subtyping". They defined strict mathematical rules for a true behavioral subtype:
Preconditions cannot be strengthened: The subtype must accept at least all the inputs the original type accepted.
Postconditions cannot be weakened: The subtype must guarantee at least all the outputs and behaviors the original type guaranteed.
Invariants must be preserved: Any underlying rule that was always true for the original type must remain true for the subtype.
If you are learning the logic behind computer science, knowing where to start can be hard. Fermi's AI tutor can work out what you already know and build a plan for the session, so you sit down to a clear next step.
Why does a square fail the substitution test?
The most famous illustration of the principle is the rectangle-square problem. In geometry, a square is a special type of rectangle. But in object-oriented programming, making a `Square` a subtype of a `Rectangle` often violates the Liskov substitution principle.
Imagine a `Rectangle` type with a contract that allows you to set its width and height independently. If you set the width to 5 and the height to 10, the area is 50. If you substitute a `Square` into the same program, it must maintain equal sides. Setting the width to 5 forces the height to 5. The program, expecting a rectangle, calculates an area of 50, but the square's area is now 25. The `Square` has broken the behavioral contract of the `Rectangle`, meaning it is not a valid subtype for this specific program.
How did behavioral subtyping change software?
Before Liskov's principle, inheritance was often treated simply as a way to reuse code. Developers would make one class inherit from another just to save typing, leading to fragile systems that collapsed when unexpected objects were passed around.
By defining strong behavioral subtyping, Liskov shifted the focus from sharing code to sharing a contract. If your code expects a list of numbers, any subtype of that list must behave exactly as the original contract promised. This guarantee allows large teams to write modular, interchangeable software without the fear of hidden side effects.
To see how other foundational ideas built modern computing, read about Donald Knuth and the analysis of algorithms or how George Boole and the Laws of Thought of 1854 formalized human reasoning.



