Design by Contract
- Categories
- Design
- Sources
- The Pragmatic Programmer
Specify the rights and obligations of a piece of code explicitly as a contract: preconditions the caller must satisfy, postconditions the routine guarantees in return, and invariants that always hold. The contract makes assumptions a stated, checkable part of the interface.
Why it Matters
Most bugs live in the gap between what a routine assumes and what callers actually provide. Writing those assumptions down turns them from implicit, accidental dependencies into an explicit agreement that can be checked, documented, and relied on, the antidote to programming by coincidence.
Signals
- Routines that silently misbehave on inputs they never promised to handle.
- Assumptions about valid input scattered as ad hoc checks or left unstated.
- Callers and callees disagreeing about whose job it is to validate.
Benefits
Clear responsibility boundaries, assumptions that are documented and verifiable, and defects caught at the violated contract rather than far downstream.
Risks
Over-specifying trivial contracts adds noise; redundant checking on every call can cost performance. Contracts are most valuable at real boundaries, not on every internal helper.
Tensions
Enforcing contracts overlaps with fail-fast (violations should be loud) yet competes with tolerant designs that absorb bad input; and a strict precondition pushes responsibility onto callers rather than defining the error away. Which approach fits depends on whether the condition is a genuine contract violation or a normal case.
Examples
A function documenting that its input must be non-empty and asserting it, so a violation crashes at the call instead of producing wrong output later; an invariant on a data structure checked after every operation during development.