CodePark/ questions / Inheritance, overriding & Liskov
START PRACTICINGPRACTICE
Backend question · Object-oriented programming › Inheritance, overriding & Liskov

S3Storage and LocalStorage both implement FileStorage. In tests (local), deleting a missing file throws ENOENT. In production (S3), it succeeds silently. Is this a Liskov problem, and how do you catch such bugs?

Yes. The Liskov Substitution Principle says any implementation must be usable wherever the base type is expected, without callers noticing. That means the same behaviour, not only the same method names.

Here callers can't rely on one rule. Code tested against LocalStorage may add a try/catch for missing files. Code written against S3 may not, and then it crashes in tests. Liskov problems often hide in details like these:

  • which errors are thrown
  • what happens with empty or missing input
  • ordering, retries and whether a call is safe to repeat (idempotent)

Fix: write the rule into the contract: "delete of a missing file succeeds." Then make LocalStorage catch ENOENT and return normally.

How to catch it: write contract tests. One shared test suite describes the contract. Run it against every implementation:

describe.each([new LocalStorage(dir), new S3Storage(client)])

If a new adapter, such as an in-memory fake, breaks the contract, a test fails right away.

  • Liskov means every implementation behaves the same for callers, not only that the methods match.
  • Differences in errors, edge cases and idempotency are common Liskov breaks.
  • Write the expected behaviour into the contract and fix the odd implementation.
  • A shared contract test suite run against every implementation catches these bugs.