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.