Notes on Structured Programming

2026-07-18 · page · source ↗ · source summary

Dijkstra's 1970 monograph that launched structured programming — the idea that programs should be composed from a restricted set of control structures (concatenation, selection, repetition) and built through step-wise refinement. More than a methodology: it's a philosophical treatise on the cognitive limits of programmers, the impossibility of testing as proof, and the case that programming is fundamentally the art of organizing complexity. Every argument about agentic code quality, verification versus testing, and the limits of human attention traces back to this document.


Key Quotes

"Program testing can be used to show the presence of bugs, but never to show their absence!"

The single most quoted line in software engineering — and one that's been swallowed, digested, and forgotten by every generation that follows. The current wave of agentic coding has revived it with a vengeance: when code is generated faster than it can be reviewed, testing alone is demonstrably insufficient. Dijkstra's corollary is that you need the program's structure to reason about its correctness — which is exactly what structured programming provides, and exactly what LLM-generated code most obviously lacks.

"Any two things that differ in some respect by a factor of already a hundred or more, are utterly incomparable."

Dijkstra opens by naming the central problem he can't solve: he wants to teach techniques for "life-size programs" but can only use small examples. The scale gap is the problem. This is the same gap that bedevils coding agent discourse today — demos work beautifully on toy problems, and everyone extrapolates to production codebases. Dijkstra's one-year-old-crawling-child versus supersonic jet analogy is the original warning against the fallacy that still powers every AI coding demo.

"The art of programming is the art of organizing complexity, of mastering multitude and avoiding its bastard chaos as effectively as possible."

Dijkstra explicitly subordinates efficiency to manageability. This was controversial in 1970 and it's still the central tension in agentic development: the machine can produce infinite code, but human understanding remains the bottleneck. His argument that efficiency optimizations should only happen after logical correctness is established — and only if the program remains "sufficiently manageable" — is the ur-text of "make it correct, then make it fast."

"I have a very small head and I had better learn to live with it and to respect my limitations and give them full credit, rather than to try to ignore them, for the latter vain effort will be punished by failure."

The entire enterprise of structured programming follows from this admission. Dijkstra treats cognitive limits not as a weakness to overcome but as the design constraint for programming methodology. This is Engineering for Bounded Cognition forty years before the term existed. The same admission explains why code review doesn't scale with AI-generated volume, why context windows aren't a substitute for structure, and why Agent Memory and Context is now the hardest problem in the field.

"Our intellectual powers are rather geared to master static relations and our powers to visualize processes evolving in time are relatively poorly developed."

The justification for making program text reflect computation structure. When program progress through text maps cleanly to computation progress through time, the static text acts as a prosthetic for the dynamic process we can't hold in our heads. This is the theoretical basis for structured programming's single-entry/single-exit discipline — and it's what every coding agent framework is stumbling toward with checkpointing, state machines, and event-sourced architectures.

"I want to view the main program as executed by its own, dedicated machine, equipped with the adequate instruction repertoire operating on the adequate variables and sequenced under control of its own instruction counter."

The layered virtual machine model — each level of abstraction is a complete machine implemented by the level below. This is the intellectual ancestor of: API design, microservices, Docker containers, MCP servers, and the entire agent abstraction stack. Dijkstra got there first, from first principles, in 1969.

Key Themes

Critical Analysis

What holds up — and what's more relevant than ever. The core arguments are unassailable and, if anything, more urgent now than in 1970. The agentic coding explosion has recreated exactly the conditions Dijkstra diagnosed: programs generated faster than anyone can understand them, correctness established by "it seems to work," and cognitive limits treated as an inconvenience rather than a design constraint. Every framework that adds structure to agent output — skills, harnesses, verification gates, checkpointing — is rediscovering structured programming at a higher level of abstraction.

The missing piece: concurrency. Dijkstra explicitly restricts himself to sequential programs. His later work (EWD310, "Cooperating Sequential Processes") addressed concurrency, but the structured programming model as presented here is purely sequential. The agents we build today are inherently concurrent — multiple models, multiple tools, multiple sessions — and the structured programming model doesn't directly apply. Yet the spirit of it — find the right primitives, restrict yourself to them, reason about the whole by reasoning about the parts — is exactly what agent architecture is groping toward.

The pearl model predicts microservices. Reading "On what we have achieved" in 2026 is uncanny. The pearl as an independent design decision, the necklace as a composition of pearls, program modification as pearl replacement — this is a precise description of what microservice architecture aspires to be and often fails to achieve. The observation that ordering pearls differently produces "messier" programs (more tightly interconnected logical threads) is a formal argument for what we now call "dependency inversion."

The weakest section is the strongest warning. The line-printer plotter example (the "second example of step-wise program composition") is tedious to read and the notation is half-baked. Dijkstra admits as much. But that's the point: he was inventing the notation as he went, and the messiness of the notation is the evidence that programming language design matters. The fact that we're still struggling with the same problem — how to express structure in a way that survives modification — suggests we haven't come as far as we think.

What Dijkstra couldn't see: the compiler as the proof. His correctness proofs are manual, laborious, and he admits they're impractical at scale. He couldn't have foreseen type systems, model checkers, or property-based testing. But his intuition was right: the structure that makes programs understandable to humans is also the structure that makes them verifiable by machines. The type checker is the automated version of Dijkstra's enumerative reasoning; the QuickCheck property is his mathematical induction, automated. For the formal logic that underlies these reasoning patterns, see An Introduction to Formal Logic (Peter Smith), which covers the same logical territory Dijkstra assumed his readers knew.

The irony nobody talks about. Dijkstra's most famous aphorism — "Go To Statement Considered Harmful" — isn't in this document. It's in EWD215 (1968). But this document is where he makes the positive case for what should replace GOTO: the three control structures and step-wise refinement. The negative case got the fame; the positive case is the actual contribution. Structured programming was never really about removing GOTO — it was about building programs from primitives that correspond to patterns of reasoning.


Sources: raw/ewd249-notes-on-structured-programming Last updated: 2026-07-18