Hello everyone, I'm Tony Bai.
In the field of software development, Martin Fowler's name is almost synonymous with a beacon of thought. Every article and speech he delivers reveals the deep implications of industry development. Recently, Master Fowler published a brief but thought-provoking blog post—"LLMs bring new nature of abstraction"—once again accurately capturing a profound change that is occurring and may subvert our perceptions and working methods.
Fowler believes that the emergence of Large Language Models (LLMs) has an impact on software development comparable to the leap from assembly language to the first High-Level Programming Languages (HLLs). But the key is that LLMs bring more than just another "higher level" of abstraction; they are fundamentally changing the "nature" of programming—forcing us to consider what it truly means to program with "non-deterministic tools." In this article, we will briefly interpret this.
From "Deterministic" Steps to "Non-Deterministic" Crossroads
Looking back at the history of programming languages, we have always pursued higher levels of abstraction to improve productivity and reduce complexity:
• Assembly Language vs. Machine Instructions: Assembly allows us to replace 0s and 1s with mnemonics, but still requires attention to specific machine registers and instruction sets.
• High-Level Languages (HLLs) vs. Assembly: Early HLLs like Fortran and COBOL allowed us to think in terms of statements, conditions, and loops, without worrying about how data moved between registers. Fowler recalls that when he programmed in Fortran IV, despite many limitations (such as IF having no ELSE, and integer variable names having to start with I-N), it was already a huge improvement.
• Modern Languages, Frameworks, DSLs vs. Early HLLs: Modern languages like Ruby, Go, and Python, as well as various frameworks and Domain-Specific Languages (DSLs), have further elevated the level of abstraction. We can now instinctively pass functions as data and use rich libraries and patterns without writing a lot of low-level code from scratch.
Fowler points out that while these developments have greatly increased the level of abstraction and productivity, they have not fundamentally changed the "nature of programming." We are still engaging in a "deterministic" dialogue with the machine: given the same input and code, we expect the same output. Bugs are also reproducible.
However, the intervention of LLMs breaks this fundamental assumption.
Fowler wrote: "Interacting with machines using prompts is as different as Ruby is from Fortran, or Fortran from assembly."
More importantly, this is not just a huge leap in abstraction level. When Fowler wrote a function in Fortran, he could compile it a hundred times, and the bug in the result would still be the same bug. But LLMs introduce a "non-deterministic abstraction."
This means that even if we store carefully designed prompts in Git, we cannot guarantee the exact same behavior every time. As his colleague Birgitta Böckeler brilliantly summarized:
We are not merely moving "up" in abstraction levels; we are simultaneously moving "sideways" into the realm of non-determinism.
The accompanying diagram in Fowler's article vividly illustrates this: traditional programming languages, compilers, and bytecode represent a clear, top-down abstraction path; while models/DSLs, code generators, low-code, and frameworks are different abstraction levels above it. Natural language (through LLMs), on the other hand, is like a path cutting in from the side, leading directly to a "semi-structured/near-human thinking" realm, which inherently carries ambiguity and uncertainty.
Challenges and Insights in the Era of "Non-Deterministic" Programming
This "non-deterministic" nature brings unprecedented challenges and questions for us Gophers, and indeed all software developers, that require rethinking:
• Version Control and Reproducibility: When prompts cannot guarantee consistent results, how do we manage and version our "AI-assisted code"? How do we ensure consistency, or at least acceptable differences, across development, testing, and production environments? Is merely versioning the prompt enough, or do we also need to version the model, parameters (like temperature), and even some critical seeds?
• Testing and Debugging: How do we test a "component" whose output is not entirely fixed? Are traditional unit testing and integration testing methods still effective? We may need to introduce new testing strategies, such as property-based testing, statistical validation of output results, or focusing more on behavior and intent validation. When LLM-generated code has issues, will debugging difficulty increase exponentially?
• Reliability and Contracts: In a system containing non-deterministic AI components, how do we define and guarantee overall reliability? How should "contracts" between services be described and enforced?
• Mindset Shift: We are accustomed to precise control over code, pursuing strict logic and predictable behavior. Now, we may need to learn to coexist with "fuzziness" and "probability," shifting from "instruction givers" to "intent communicators" and "result filters."
What Does This Mean for Us Gophers?
Go language is highly favored by developers for its clarity, strong typing, concise concurrency model, and relatively predictable behavior. When we try to integrate LLMs into Go's ecosystem and development process, these "non-deterministic" characteristics bring new considerations:
• AI-Generated Go Code: When we use LLMs to generate Go code snippets, unit tests, or even entire modules, how do we ensure that the generated code conforms to Go's best practices, is efficient, and secure? How do we effectively review and integrate the generated code?
• Building Tools/Agents with Go that Interact with LLMs: If we develop backend services or Agents in Go that interact with LLMs, we need to fully consider the non-determinism of LLMs in architectural design, designing more robust error handling, retry mechanisms, and validation and filtering logic for LLM output.
• Using LLMs to Understand Complex Go Systems: LLMs might help us understand complex legacy Go codebases, but the accuracy and consistency of their interpretations also need careful evaluation.
Fowler expressed his excitement about this change at the end of the article: "This change is dramatic and exciting for me. I'm sure I'll grieve some things that are lost, but we will gain things that few of us can comprehend."
Conclusion: Embrace Uncertainty, Explore New Continents
Martin Fowler's article reveals a profound shift in programming paradigms that may occur in the LLM era. It is no longer just an evolution of tools, but a fundamental change in how we collaborate with machines.
As Gophers, as software engineers, we need to start seriously considering the impact of this "non-determinism" and actively explore new methods to coexist with it, or even leverage its characteristics to create value. This is undoubtedly a new continent full of challenges but also opportunities.
What do you think of Fowler's view? What specific impacts do you think the "non-determinism" brought by LLMs will have on your daily development work? Feel free to share your thoughts in the comment section!
Resource Link: https://martinfowler.com/articles/2025-nature-abstraction.html