Developer Productivity
The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming"
The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming" In 1978, Edsger W. Dijkstra penned a seminal paper titled "On the foolishness of '
The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming"
In 1978, Edsger W. Dijkstra penned a seminal paper titled "On the foolishness of 'natural language programming'" (Source 1, 4). His prescient critique, published decades before the advent of modern large language models (LLMs), offers profound insights that resonate with striking clarity in today's AI-driven world (Source 6). Dijkstra's warning serves as a crucial reminder for anyone navigating the complexities of AI agents and human + AI collaboration within the contemporary AI office, underscoring the timeless importance of precision and formalism over the allure of vague, natural language instructions.
01Dijkstra's Prophecy: The Original Critique of "Natural Language Programming"
Dijkstra's 1978 paper addressed a persistent sentiment among early computing enthusiasts: the belief that programming's requirement for
Want your team to run this workflow with AI-native execution?
precision and formal notation would eventually disappear if computers could simply understand ordinary human speech. Dijkstra rejected this hope not because natural language lacks expressive power, but because it is designed for communication between people who share context, assumptions, and the ability to repair misunderstandings.
A computer program has a different requirement: its meaning must be sufficiently exact that a machine can execute it repeatedly and predictably. The central problem is therefore not whether a computer can parse a sentence. It is whether the sentence determines one unambiguous behavior across all relevant circumstances.
02Why ordinary language is a poor specification
Natural language is efficient partly because it leaves things unsaid. When someone says, “Close the window,” a human listener normally infers which window is intended, whether it is already closed, and whether the request is serious. If the listener is uncertain, the conversation can continue:
“Which window?”
“The one in the conference room.”
“Should I lock it?”
This flexibility is useful in everyday life. In software, however, each unanswered question can become a defect. What should happen if the window is missing? What if two windows match? What if the operation fails halfway through? What if another process changes the relevant state at the same time?
Programming languages impose rules that make such questions visible. A type system, grammar, interface definition, or contract may appear cumbersome compared with a casual instruction, but that apparent burden is often the cost of exposing hidden assumptions. Formal notation does not eliminate the need for judgment; it makes the points requiring judgment harder to overlook.
Consider the instruction:
“Transfer the funds when the account has enough money.”
This sentence leaves many details unresolved:
- Is “enough money” measured before or after fees?
- Does the transfer include pending transactions?
- What happens if two transfers are initiated simultaneously?
- Should the transfer be rejected, delayed, or partially completed?
- Is a negative balance permitted?
- Which currency and rounding rules apply?
- What if the recipient account is closed?
A natural-language request can be an appropriate starting point for discussing a system. It is not, by itself, a reliable definition of the system.
03Ambiguity is not the only problem
The weakness of natural-language programming extends beyond ambiguous words. Even a sentence that appears clear may fail to specify important properties of a program.
One distinction is between what a system should do and how it should do it. “Find the shortest route” describes a goal, but not necessarily the acceptable algorithm, performance limit, treatment of ties, or definition of distance. A human may fill in those details from context. A production system cannot safely rely on all users making the same assumptions.
Another distinction is between typical cases and exceptional cases. Human instructions often describe the normal path:
“Read each customer record and send a confirmation email.”
A robust implementation must also account for malformed records, duplicate addresses, unavailable mail servers, rate limits, retries, privacy restrictions, and the possibility that the process stops after sending only some messages. These are not peripheral concerns. In many systems, error handling determines whether the software is trustworthy.
There is also the problem of scale. A person can interpret “show the most recent orders” in a small conversation. A database system needs a precise ordering, a time zone, a definition of “order,” and a policy for records with equal timestamps. At a million records, an inefficient interpretation may be unusable even if it is logically correct.
Dijkstra’s broader point was that programming is an activity of constructing and verifying a precise object. Replacing symbols with familiar prose does not remove the underlying complexity. It can conceal that complexity until implementation or testing exposes it.
04The difference between instruction and intention
A request usually expresses an intention, not a complete algorithm. This distinction matters when evaluating both conventional software and AI-generated code.
Suppose a manager asks:
“Make the report highlight unusual spending.”
A programmer may reasonably ask what counts as unusual. Is it an amount above a fixed threshold, a deviation from a department’s historical average, or a transaction unlike the employee’s usual behavior? Should seasonal purchases be treated differently? Should the system explain its decision?
An AI assistant can generate a plausible implementation immediately. That speed may be helpful, but it does not answer the unresolved questions. The assistant may silently choose a threshold, a statistical model, or a data source. The resulting code can look finished while encoding assumptions nobody explicitly approved.
This is one reason natural-language interfaces can create a dangerous illusion of progress. A fluent response is not evidence that the requirements are complete. Nor is syntactically valid code evidence that the intended behavior has been captured.
The appropriate response to an underspecified request is often not immediate execution. It is clarification, decomposition, and the production of explicit acceptance criteria. In that sense, a useful AI programming assistant should sometimes behave less like a typist and more like a careful reviewer.
05What large language models change—and what they do not
Large language models make Dijkstra’s warning newly relevant because they are exceptionally capable at translating between human language and formal artifacts. They can produce code, tests, queries, configuration files, and documentation from short prompts. This is a major practical advance, but it does not overturn the underlying argument.
An LLM predicts text based on patterns learned from data. It can often infer conventional interpretations and produce a useful first draft. It does not automatically possess the project’s complete operational context, nor does it guarantee that its interpretation matches the user’s unstated intent. A model may confidently choose one meaning among several reasonable meanings.
For example, the prompt:
“Create a service that deletes inactive users.”
requires decisions about inactivity, retention obligations, account recovery, legal holds, backups, audit records, and authorization. A model can generate an endpoint and a database query in seconds. The difficult work is determining whether deletion is actually permitted, what “inactive” means, and how the operation can be reversed or audited.
The problem is not that the model uses natural language. Human developers use natural language throughout the engineering process. The problem arises when conversational fluency is mistaken for specification, verification, or authorization.
LLMs can therefore be valuable at several stages:
- Elicitation: suggesting questions that expose missing requirements.
- Drafting: translating a reasonably precise design into code.
- Inspection: identifying suspicious patterns, edge cases, and inconsistencies.
- Testing: proposing test cases, including boundary and failure conditions.
- Documentation: explaining an existing implementation in accessible language.
At every stage, human judgment and executable checks remain necessary. The model can help make assumptions visible; it cannot make an ambiguous requirement precise merely by producing more prose.
06Formalism as a practical discipline
Formal methods are sometimes portrayed as an extreme alternative to ordinary engineering: mathematically elaborate techniques suitable only for safety-critical systems. In practice, formalism exists on a spectrum.
At the lighter end are explicit data schemas, type annotations, preconditions, invariants, state diagrams, API contracts, and property-based tests. These tools force a team to state what values are permitted and what must remain true. At the stronger end are formal specifications, model checking, theorem proving, and mechanically verified implementations.
A simple invariant can be more valuable than a page of informal explanation. In a banking system, for example:
The total balance across all accounts and the ledger must remain conserved, except for explicitly recorded external deposits, withdrawals, and fees.
That statement can guide design, testing, logging, and review. It also gives an AI assistant a constraint against which proposed code can be examined.
Similarly, an access-control rule should not remain merely “users can view their own records.” It should be expressed in terms that can be tested: a request is authorized only when the authenticated principal is associated with the requested record through an approved relationship, subject to administrative and legal restrictions. The more consequential the system, the more valuable such precision becomes.
Formalism does not mean turning every conversation into mathematical notation. It means recognizing which claims must be exact and giving them a representation that can be checked.
07Tests are not a substitute for clear meaning
Testing provides an essential safeguard, but tests cannot fully rescue an undefined requirement. A test demonstrates that a particular input produced an expected output. If the expected output was chosen from an incorrect interpretation, the test may simply confirm the wrong behavior.
Imagine a team implementing “sort customers by priority.” It writes tests for high, medium, and low priority and all tests pass. Later it discovers that priority should have been determined by contractual service level rather than by an internal numerical field. The implementation was correct relative to the tests but incorrect relative to the business requirement.
This is why testing should include more than examples. Boundary cases, failure cases, security properties, concurrency behavior, and invariants help reveal interpretations that ordinary examples leave hidden. Reviews with domain experts are also important because many requirements are semantic rather than syntactic.
For AI-generated code, tests are especially important because generated code often resembles familiar solutions. Familiarity can be misleading: a routine may compile, pass a few examples, and still mishandle authorization, resource exhaustion, numerical limits, or adversarial input.
08Natural language still has an important role
Rejecting natural-language programming does not mean rejecting natural language from software development. Prose is indispensable for expressing goals, discussing trade-offs, recording decisions, and communicating with people who do not write code. The mistake is treating prose alone as a complete and executable specification when precision is required.
A productive workflow moves between levels:
- State the goal in ordinary language.
- Identify actors, inputs, outputs, constraints, and failure modes.
- Resolve ambiguous terms and document assumptions.
- Express critical rules in structured or formal notation.
- Implement the design.
- Test both examples and general properties.
- Monitor the deployed system and revise the specification when reality changes.
In this workflow, natural language begins the conversation but does not end it. The transition from intention to dependable behavior requires progressively more explicit representations.
09The enduring lesson
Dijkstra’s warning remains valuable because it challenges a recurring technological fantasy: that the hardest part of programming is communicating with a machine in the wrong vocabulary. If only computers could understand ordinary language, the fantasy suggests, the difficulties of programming would disappear.
But programming is difficult because the world is complicated, requirements conflict, edge cases matter, and systems must behave correctly even when their creators are absent. Changing the interface from a programming language to a conversational prompt does not remove those conditions. It may make them easier to ignore.
Modern AI can reduce the cost of producing code and increase the accessibility of computational tools. That is significant. Yet the value of the result still depends on the precision of the objective, the quality of the constraints, and the strength of the verification process. A model may turn a vague request into an impressive artifact; only disciplined engineering can establish whether the artifact is the right one.
The most constructive interpretation of Dijkstra’s argument is therefore not “never use natural language.” It is “do not confuse natural language with precision.” Use conversation to discover intent, formalism to state critical behavior, code to realize it, and tests and proofs to challenge it. In an AI-assisted workplace, this division of labor is more important than ever.
The future of programming may involve fewer hand-written lines and more interaction with intelligent tools. It will not involve fewer requirements, fewer assumptions, or fewer consequences for getting those assumptions wrong. The enduring wisdom is to make meaning explicit before trusting a machine—or an AI agent—to act on it.
For The foolishness of "natural language programming" (1978), Nonilion can be used as the practical AI-office example: a shared workspace where human teammates and AI agents keep discussion, decisions, and execution connected.
The reason The foolishness of "natural language programming" (1978) keeps returning to Nonilion is simple: the topic becomes more useful when it turns into coordinated work, not just another article, chat, or dashboard.
10Why This Trend Matters for Nonilion
This trend matters to Nonilion because it points to a bigger change: teams are moving from simple calls toward persistent, AI-supported collaboration spaces. Nonilion can bridge live presence, meeting context, avatars, and follow-up work so the trend becomes a usable workflow instead of a headline.
11Shareable Extracts
- The trend is not just "The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming"" - it is a signal that team coordination is becoming the next competitive edge.
- Hot take: the teams that win from this shift will not be the ones with more meetings; they will be the ones with clearer shared context after every meeting.
- If the enduring wisdom of dijkstra's 1978 warning: the foolishness of "natural language programming" keeps moving this fast, remote teams need a workspace where conversation, presence, and follow-up stay connected.
- The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming" In 1978, Edsger W.
- Dijkstra penned a seminal paper titled "On the foolishness of 'natural language programming'" (Source 1, 4).
12Social Hooks
- Everyone is talking about The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming". The overlooked part is what happens to team workflows after the headline fades.
- The uncomfortable question behind The Enduring Wisdom of Dijkstra's 1978 Warning: The Foolishness of "Natural Language Programming": are teams adapting their collaboration systems fast enough?
- This is not a meeting trend. It is a coordination trend, and products like Nonilion sit right in the middle of that shift.
13Sources and Author
Sources
-
On the foolishness of "natural language programming ... www.cs.utexas.edu/~EWD/transcriptions/EWD06xx/EWD667.html
-
The foolishness of "natural language programming" (1978) news.ycombinator.com/item
-
LLMs, programming and re Dijkstra's On the foolishness of ... medium.com/@Szypetike/llms-programming-and-re-dijkstras-on-the-fool...
Author
This article on The foolishness of "natural language programming" (1978) was generated by the Nonilion AI blog workflow using web research inputs and AI-assisted synthesis.










