Topics / Engineering practice

Design by contract makes caller and implementation obligations explicit.

State the conditions a caller must satisfy, the result a component promises and the invariants it must preserve. Then decide how each claim will be checked.

Write a contract that can expose a mistake.

Illustrative example

Transfer between two accounts

Contract partIllustrative obligation
PreconditionThe amount is positive and the source has sufficient funds
PostconditionThe source decreases and destination increases by that amount
InvariantThe transfer does not create or destroy value within the modeled pair

Real payment systems have fees, holds, concurrency and external effects. Add those conditions explicitly rather than relying on this simplified example. A precise contract is useful because it makes omissions discussable.

Distinguish checks from proofs.

A runtime assertion checks a condition during an execution. A test exercises selected cases. A formal argument establishes a property within a model and its assumptions. None of these automatically validates that the contract expresses the right business rule.

Be careful with preconditions that exclude real failure conditions. Assuming the network never duplicates a request can make an elegant model irrelevant to the retry behavior you need to verify.

Keep contracts connected to requirements.

Proof’s requirement graph can retain the higher-level promise, component obligations and associated evidence. Formalization and tests need a reviewed relationship to the actual implementation and environment.

Start with one boundary where misunderstanding is expensive. Specify errors and side effects as carefully as the successful return value.

Try it on your software

Bring one component boundary with an ambiguous contract.

We will identify the caller obligations, promised result and evidence needed to check it.