Gries’ closing chapters make a quiet but radical point: the assertions you invented to prove a program correct are its best documentation — and they sit at the end of a lineage that runs Floyd → Hoare → Dijkstra → Gries, from “verify after” to “derive correct by construction.”
{P} S {Q} for a Hoare triple, wp for weakest precondition, invariants and bound functions for loops. See the book for the full, rigorous treatment.Ask what a comment is worth and you get an uncomfortable answer: a comment records what someone hoped the code did at the moment they wrote it. It is prose, unchecked, and it rots the instant the code beneath it changes. Gries’ whole method hands you something categorically better. The precondition, the postcondition, and above all the loop invariant are not decoration bolted on afterward — they are the very predicates the program was derived from. They state intent precisely, in the same logic the code obeys, and they are checkable.
That is the shift in status. The pre/postcondition pair is the contract: it tells a reader exactly what the code assumes and exactly what it promises, without their having to read the body. The invariant tells them the idea of the loop — the single fact that stays true across every iteration — which is the one thing raw code never reveals. These assertions are the most valuable documentation a program can carry precisely because they are the documentation the compiler, the test suite, and a proof can all hold you to.
A loop is the hardest construct to read cold, because its meaning lives in what accumulates over many iterations, not in any single line. Gries’ prescription is exact: next to every loop, record four things — the precondition that holds on entry, the invariant that holds before and after each iteration, the bound function that proves it terminates, and the postcondition it establishes on exit. Those four are not optional annotations; they are the loop’s specification.
q = 0
r = a
# loop through items
while r >= b:
r = r - b
q = q + 1
# why does this compute a / b ?
# what stays true each pass?
# why does it stop? the reader
# must reverse-engineer all of it.# pre Q: a ≥ 0 and b > 0
q = 0
r = a
# invariant P: a = q*b + r and 0 ≤ r
# bound t: r (t ≥ 0, t decreases)
while r >= b: # guard B
r = r - b
q = q + 1
# post R: a = q*b + r and 0 ≤ r < bRead the two versions side by side. The left tells you the shape of the code you can already see. The right tells you the four things the code cannot tell you: what it needs (a ≥ 0, b > 0), the exact relationship it maintains (a = q*b + r ∧ 0 ≤ r), why it halts (r is a non-negative integer that strictly drops each pass), and what it delivers (0 ≤ r < b). Anyone can now verify the loop without running it — on exit, invariant plus negated guard (0 ≤ r and r < b) yields the postcondition directly. The same skeleton documents a summation loop: invariant s = (sum of A[0..i-1]) ∧ 0 ≤ i ≤ n, bound n - i, post s = sum of A[0..n-1].
The discipline distills into a handful of rules. None are about volume — more comments is not better documentation. They are about recording the things the code genuinely cannot say for itself, and never the things it already says plainly.
| Rule | Why | Smell it replaces |
|---|---|---|
| Document the WHAT / the intent, not the obvious HOW. | The mechanics are already in the code. The intent — the relationship being maintained, the contract being met — is not. | i = i + 1 # increment i |
| Keep assertions honest; update them with the code. | A stale assertion is worse than none — it actively misleads. When the code changes, the invariant changes with it, or the derivation no longer holds. | A postcondition that describes last quarter’s behaviour. |
| Name variables for their role in the invariant. | If i is “number of elements already processed,” the invariant reads itself. Good names make the assertions almost redundant — the best outcome. | x, tmp, flag carrying real meaning. |
| A procedure is documented by its spec, not its body. | A caller should need only the pre/postcondition to use it correctly. If they must read the implementation, the specification has failed. | “See the code for what it returns.” |
| Avoid comments that merely restate code. | They double the maintenance surface and add zero information. Every comment must earn its place by saying something the code cannot. | # set total to zero above total = 0. |
Here the documentation stops being passive. The very predicate that proves a loop correct can be dropped into the running program as an assert — a runtime check that fails loudly the moment reality diverges from the proof. The invariant that lived in a comment becomes an executable guard against regression.
def divide(a, b):
assert a >= 0 and b > 0 # pre Q
q, r = 0, a
while r >= b:
assert a == q*b + r and r >= 0 # inv P
r -= b
q += 1
assert a == q*b + r and 0 <= r < b # post R
return q, r# property-based test: for ANY valid input
# the postcondition must hold. The oracle is
# the specification itself, not a table of
# hand-picked expected outputs.
def test_divide(a, b): # a ≥ 0, b > 0
q, r = divide(a, b)
assert a == q*b + r
assert 0 <= r < b
# one predicate certifies every caseThis is the bridge to modern practice. Design by contract (Eiffel, and the require/ensure/invariant constructs it inspired) is Gries’ pre/post/invariant triad promoted to first-class language features that execute. Property-based testing (QuickCheck, Hypothesis) uses the postcondition as an oracle across machine-generated inputs, so the specification checks thousands of cases you would never enumerate by hand. Neither replaces a proof — an assertion that never fires proves nothing about the paths it did not take, and tests still show only the presence of bugs. But the same predicate serves in three roles at once: it proves the code, documents the code, and guards the code against future change. That triple duty is the payoff.
Gries did not invent this calculus; he synthesized and taught it. The closing notes trace a fifteen-year arc in which the field’s ambition shifted from checking a finished program to growing a correct one. Four papers carry the story.
| Year | Who & what | The contribution |
|---|---|---|
| 1967 | Robert W. Floyd “Assigning Meanings to Programs” | Attaches assertions to the edges of a flowchart and derives verification conditions: if each assertion implies the next across every node, the program is correct. Introduces the well-ordered / bound idea — a quantity that strictly decreases in a well-founded order — to prove termination. The first rigorous framework for what a program means. |
| 1969 | C.A.R. Hoare “An Axiomatic Basis for Computer Programming” | Turns Floyd’s idea into a formal logic. The {P} S {Q} triple becomes the central object, and each language construct gets an axiom or inference rule (assignment, composition, conditional, the while-rule with its invariant). This is Hoare logic — correctness as formal deduction. |
| 1975–76 | Edsger W. Dijkstra “Guarded Commands, Nondeterminacy and Formal Derivation of Programs” (1975) & A Discipline of Programming (1976) | Replaces verification-after with derivation. The weakest precondition wp(S,R) is a predicate transformer computed backward from the goal; guarded commands (if…fi, do…od) build nondeterminacy in cleanly. Now you calculate the program from its specification rather than guessing and checking. |
| 1981 | David Gries The Science of Programming | Synthesizes and teaches the method to working programmers. Supplies the logic prelude, makes the calculational, predicate-transformer discipline learnable, and adds the practical heuristics — inventing invariants by weakening the postcondition — that turn theory into a repeatable way of writing code. |
The single most important movement across this table is the change in when correctness enters. Floyd and Hoare give you tools to verify after: write the program, then prove it meets its spec. Dijkstra and Gries invert this into derive correct by construction: let the spec and the emerging proof drive the code so it is right the first time. The bound function that Floyd introduced for termination survives untouched all the way to Gries — a reminder that the ideas accreted rather than replaced one another.
Notation dates; the discipline does not. Few programmers today write do…od or compute a wp by hand, yet the habits of mind the calculus instills are exactly the ones that separate reliable code from hopeful code.
wp derivation or just a one-line invariant comment above a for loop, you are practising the same craft Floyd, Hoare, Dijkstra, and Gries built: making a program’s meaning explicit, checkable, and correct by design.| Idea | The one-line takeaway |
|---|---|
| Assertions as documentation | A comment says what someone hoped; an invariant says what must be true — precise and checkable. |
| Documenting a loop | Record four things beside every loop: precondition, invariant, bound function, postcondition. |
| Guideline: intent over mechanics | Document the WHAT, never the obvious HOW; the code already owns the how. |
| Guideline: honest assertions | A stale assertion misleads — update it with the code or delete it. |
| Guideline: spec over body | A procedure is documented by its specification; a caller should never need to read the body. |
| Executable assertions | The predicate that proves a loop can run as an assert and serve as a test oracle. |
| Bridge to modern practice | Design by contract and property-based testing are Gries’ triad promoted to first-class tooling. |
| Floyd (1967) | Flowchart assertions, verification conditions, and the bound idea for termination. |
| Hoare (1969) | The {P} S {Q} triple and an axiom per construct — correctness as formal deduction. |
| Dijkstra (1975–76) | Weakest preconditions and guarded commands — derive the program, do not just check it. |
| Gries (1981) | Synthesizes and teaches the calculational discipline, with heuristics for inventing invariants. |
| The historical shift | From “verify after” (Floyd, Hoare) to “derive correct by construction” (Dijkstra, Gries). |
| What outlives the notation | Think in invariants, let the spec lead, prove termination, keep assertions living. |