Design by Contract
Specifying what each function requires and guarantees, as checkable preconditions, postconditions, and invariants, so a violation says exactly whose bug it is.
Design by contract treats each function or class as a party to a contract. The caller must satisfy certain conditions, and in return the function guarantees certain results. Bertrand Meyer developed the approach in the mid-1980s and built it into his language Eiffel, drawing on Tony Hoare's logic of program correctness. A contract has three parts:
- Preconditions are what must be true when the function is called, such as "the list is not empty" or "the amount is positive." They are the caller's obligation.
- Postconditions are what the function guarantees on return, if the preconditions held, such as "the result is sorted" or "the balance decreased by exactly the amount."
- Invariants are properties of an object that hold between every public operation, such as "the balance is never negative" or "the cached count matches the number of items."
The main payoff is assigning blame. When a precondition fails, the caller has a bug. When a postcondition or invariant fails, the function has a bug. Debugging starts in the right place instead of somewhere downstream where corrupted data finally caused a crash. Checking contracts at runtime is a form of Fail Fast: an Assertion at the boundary stops the program at the first broken promise.
Contracts also document intent precisely. A signature says a function takes an integer. A contract says which integers, and what happens to them. That gives other tools something to check against. Static Analysis tools can try to prove contracts hold, Formal Verification proves them outright, and Property-Based Testing can use postconditions and invariants directly as the properties to test across random inputs.
Few mainstream languages build contracts in. Eiffel, Ada, D, and Racket do. Elsewhere the idea appears piecemeal: assertions, validation at API boundaries with Schema Validation or a JSON Schema, and types that make invalid states unrepresentable, so a precondition becomes a type the compiler enforces. Immutability helps by ensuring an invariant established at construction can't be broken later.
Design by contract takes a deliberate stance against defensive programming everywhere. If a function's precondition says the input is valid, the function needn't check again, which puts the responsibility in one place. That contrasts with Postel's Law, which advises being liberal in what you accept. Contracts favor strictness, at least within a codebase.
Contracts between functions are different from Contract Testing, which checks that separate services agree on the shape of their API, though the idea of a mutual, checkable agreement is the same.
See also3
Database Constraint
Rules declared in the schema that the database itself enforces on every write, such as uniqueness, required values, and allowed ranges, so bad data is refused at the door rather than cleaned up later.
Data & Content11 connections
Test Oracle
Whatever decides whether a test's output is correct, and the often-overlooked hard part of testing when nobody knows the right answer in advance.
Testing & Verification11 connections
Defensive Programming
Writing code that anticipates misuse and bad input, checking assumptions and handling the unexpected rather than trusting callers, along with the debate over when defense should give way to failing loudly.
Testing & Verification11 connections
Linked from7
- AssertionTesting & Verification
The statement in a test that must hold, and the only part that can actually fail.
- Database ConstraintData & Content
Rules declared in the schema that the database itself enforces on every write, such as uniqueness, required values, and allowed ranges, so bad data is refused at the door rather than cleaned up later.
- Defensive ProgrammingTesting & Verification
Writing code that anticipates misuse and bad input, checking assumptions and handling the unexpected rather than trusting callers, along with the debate over when defense should give way to failing loudly.
- Property-Based TestingTesting & Verification
Asserting a general property that should hold for all inputs, then letting the framework generate many random inputs to try to break it.
- Schema ValidationAgents & Language Models
Checking data against a declared shape at runtime, and using the result as the typed value.
- Static AnalysisTesting & Verification
Finding bugs by examining code without running it, from linters and type checkers to deep security analysis, always trading missed bugs against false alarms.
- Test OracleTesting & Verification
Whatever decides whether a test's output is correct, and the often-overlooked hard part of testing when nobody knows the right answer in advance.