Martin Fowler's Warning: LLMs Are Not a Higher Abstraction, but a Different Kind of Abstraction
> Original video: Better Stack — "Does programming still need 'precision'? Martin Fowler warns software development is undergoing its biggest upheaval!" > Core source: Martin Fowler's article "LLMs and the future of software development" > Video link: https://academy.claude-code.club/
Core Claim
Martin Fowler argues that the impact of LLMs on software development is not "yet another layer of abstraction" but a fundamental paradigm shift. Traditional programming abstractions are deterministic — you write code, and the computer executes it precisely. The abstraction LLMs introduce is non-deterministic — you describe intent, and the model produces a probably-correct result that can drift. This means the core skill of programmers is shifting from "precise control" to "deviation management."
Three Key Points
1. Not a higher abstraction, but a *different* abstraction
The history of programming languages is a history of abstraction:
- Machine code → assembly → C → Python → low-code platforms
- Each layer moves programmers further from hardware and closer to the problem
- Unit testing: input A must produce output B
- Type systems: the compiler guarantees data-structure consistency
- Formal verification: mathematical proofs of correctness
- Intent engineering: translating vague requirements into clear, low-ambiguity instructions for the LLM
- Deviation management: recognizing the LLM's common error patterns and building detection and fallback mechanisms
- Verification systems: using tests, audits, and sandboxes to guarantee quality when LLM output cannot be fully trusted
- Martin Fowler's original article: https://martinfowler.com/articles/2025-llm-software-development.html (or a similar recent article)
- Claude Code Academy: https://academy.claude-code.club/
- Related concepts: Prompt Engineering → Intent Engineering → Deviation Management
But Fowler says LLMs are not a continuation of this chain. They are not "a more advanced language" but conversational intent expression. The differences:
| Dimension | Traditional abstraction (e.g., Python) | LLM abstraction | |---|---|---| | Determinism | 100% — code that says A does A | Probabilistic — the same prompt may give different results | | Explainability | Every instruction has clear semantics | Black-box reasoning, internal logic untraceable | | Debugging | Breakpoints, logs, step-through | Prompt engineering, examples, constraints, iteration | | Failure mode | Explicit errors (syntax/type/runtime) | Silent deviation (looks right, but is wrong) | | Controllability | Precise control of every branch | Guidance only via constraints and examples |
This isn't a matter of being "higher" — the nature has changed.
2. Non-determinism enters the core of software development for the first time
Traditional engineering methods rest on determinism:
LLMs break this. A prompt cannot guarantee consistent output, or even semantic equivalence. The core problem of software development shifts from "how to write correct code" to "how to manage a non-deterministic component." Fowler cites a key observation: this is not the first time non-determinism has appeared in software (randomness, concurrency, networks all exist), but it is the first time non-determinism appears in the production of code itself.
3. (Most important) From "writing correct code" to "managing deviation"
The traditional workflow: understand requirements → design algorithms/architecture → write code → debug until correct.
In the LLM era:
1. Understand requirements 2. Define intent (natural language or structured prompts) 3. Let the LLM generate code/solutions 4. Verify the results (tests, review, boundary checks) 5. Define deviation boundaries (when the LLM errs, and how to fall back)
The programmer's new skill tree:
Why This Matters
1. The industry is underestimating the shift. Many treat LLMs as "a faster copilot" — still coding, just with someone typing for you. Fowler says this is too shallow: LLMs change not the *speed* of writing code but the *source of code* and how quality is assured.
2. The definition of "programming" is expanding. When code is generated by LLMs, the programmer's focus moves upstream (precise requirements, context management, constraint design) and downstream (verification, deviation detection, fallback strategies). The middle "writing code" step gets automated, but complexity at both ends increases.
3. Engineering methods for non-determinism are immature. We have 70 years of deterministic-programming experience (compilers, debuggers, type systems), but managing non-deterministic components is in its infancy: prompt version control barely exists; consistency testing of LLM output is nearly blank; regression testing for prompt changes has no standard solution; multi-model A/B testing is experimental. The real bottleneck of LLM programming is not model capability but engineering methodology.
Three Overlooked Risks
1. "Looks correct" is more dangerous than "fails loudly." Traditional bugs crash or throw errors. LLM bugs are silent — the code runs but the logic is wrong, and errors may surface only under edge conditions. This changes the nature of software safety.
2. Context drift. LLM output depends on context (conversation history, RAG retrieval, system prompts), which drifts over time and with data updates. Traditional version control tracks code versions, but an LLM's "version" is the combination of context + model + data — no mature tooling exists for this.
3. Skill atrophy. If a new generation of programmers mainly generates code with LLMs without understanding fundamentals: can they debug LLM-generated errors? Judge whether generated code is safe? Write correct code manually if the LLM fails? Fowler doesn't say this explicitly, but it's an implicit concern: LLM tools may weaken rather than strengthen foundational skills unless deliberately maintained.
Three Action Items for Programmers (distilled from the video)
1. Shift from "writing code" to "defining boundaries." Don't just focus on how to write prompts; ask: what errors are unacceptable in this task? How do you detect them? What is the fallback when the LLM fails?
2. Build verification habits. Treat the LLM as an untrusted intern. It may be capable, but you need to: review every piece of generated code, ensure test coverage for every feature (especially LLM-generated parts), and maintain an "LLM deviation list" recording where the model tends to fail.
3. Keep low-level skills. Use LLMs for efficiency, but deliberately maintain: the ability to hand-write complex algorithms, read and understand low-level code, and debug and reason independently. These are the fuses for when the LLM fails.