Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 43

Deterministic vs. Non-Deterministic Systems: Architectural Challenges

Recommended resources

Introduction

In our previous lesson, we surveyed the landscape of AI capabilities across the software development lifecycle, creating a map of what AI can do—from decision support and documentation to code generation and analysis. This provided a crucial foundation for understanding AI's potential impact.

Now, we pivot from the "what" to the "how." This lesson addresses a fundamental property of modern AI systems that presents one of the most significant challenges to software architecture in decades: non-determinism. Your entire career in software engineering has been built on the bedrock principle that given the same input, a program produces the same output. AI, particularly large language models, shatters this assumption.

Our learning outcome for this session is to distinguish between deterministic and non-deterministic system components and the architectural challenges of combining them. We will explore why determinism is so vital, how AI's probabilistic nature breaks our traditional mental models, and—most importantly—what architectural patterns we can use to build reliable, predictable systems that leverage these powerful but unpredictable components.


The Foundation: Determinism in Software Engineering

For an experienced software engineer, determinism is so deeply ingrained that it's often taken for granted. It’s the invisible foundation that allows us to test, debug, and evolve complex systems with confidence. Before we explore the challenge of non-determinism, it's essential to consciously re-examine why determinism is the cornerstone of robust engineering.

The Simplest Way to Make Your Architecture Testable and Reproducible (Works Every Time)

Dave Farley, a co-author of Continuous Delivery, offers a concise and powerful argument for the central role of determinism in modern software engineering.

Watch the first two sections of the video. First, from the beginning to the introduction, where he defines determinism as the "prerequisite for trust." Then, watch the next part discussing time and concurrency as common sources of non-determinism and the classic techniques used to manage them, such as injecting clocks and using immutable state.

As Farley argues, determinism enables repeatability, which is essential for measurement and, ultimately, for building an evolutionary architecture. Your experience with troubleshooting race conditions or bugs that only appear at specific times of day are practical encounters with the pain of uncontrolled non-determinism.

Of course, no complex system is perfectly deterministic. We've always dealt with external factors that introduce variability. The diagram below illustrates some of these traditional sources.

This diagram shows various sources of non-determinism in a system, including physical factors (e.g., hardware), emergent behavior, concurrency, and exceptional events like failures and faults. AI introduces a new, potent form of emergent non-determinism directly into the application logic.

Historically, our architectural strategies have focused on pushing these sources of non-determinism to the edges of our systems—isolating database interactions, network calls, and other side effects—to keep our core business logic pure and predictable.


The New Challenge: AI as a Non-Deterministic Collaborator

AI, and specifically Large Language Models (LLMs), introduces non-determinism not at the edges, but at the very core of logical processing. The same prompt can yield different results. Small input variations can cause large output changes. This isn't a bug; it's an inherent feature of how these probabilistic systems work.

How AI will change software engineering – with Martin Fowler

In this interview, Martin Fowler, another influential voice in software design, discusses the impact of AI.

First, listen to his opening statement at the very beginning. Then, watch the segment from his main point on how the shift to non-determinism is the biggest change. Finally, watch the exchange from this confirmation, where the host and Fowler agree that this is likely the first time the industry is facing this challenge at such a wide scale.

Fowler's emphasis underscores the gravity of this shift. Our established practices for everything from testing to retries are built on assumptions that AI violates. The following article provides an excellent breakdown of these challenges.

AI Is Not a Library: Designing for Nondeterministic Dependencies

This O'Reilly Radar article articulates exactly why treating an LLM like a typical API or library is a misleading and risky architectural choice.

Read the article from the beginning down to the end of the section on guardrails. Pay close attention to these key points: How AI's non-determinism breaks conventional retry mechanisms. The concept of "acceptable but wrong" outcomes and their challenge to traditional observability. The critical mindset shift from designing for correctness to designing for acceptability. The move from relying on guarantees to implementing guardrails.

The article makes it clear that we cannot simply plug an AI component into our existing architectures and expect things to work reliably. Attempting to force deterministic behavior from an inherently probabilistic system is futile. The architectural challenge, therefore, is to design systems that can contain and manage this variability.


Architectural Patterns for Taming Non-Determinism

So, how do we build robust systems with non-deterministic components? We can start by adapting a familiar pattern. In the video from Dave Farley, he introduced the concept of separating the code that decides from the code that acts.

The Simplest Way to Make Your Architecture Testable and Reproducible (Works Every Time)

Let's revisit Dave Farley's video to look at a classic pattern for managing side effects.

Watch the section from his description of the "deterministic core" and "imperative shell" pattern. This is a form of Hexagonal Architecture that isolates non-determinism.

This pattern of a deterministic core surrounded by an imperative shell is our starting point. The core contains pure, testable logic, while the shell handles messy, unpredictable interactions with the outside world (networks, databases, clocks).

When integrating an LLM, we can think of the LLM itself as a new, powerful component within that imperative shell. However, the LLM isn't just performing a simple side effect; it's a reasoning engine. A more advanced architectural approach treats the LLM as a "non-deterministic kernel process" that is wrapped and controlled by a "deterministic runtime environment."

The following article from Praetorian details a sophisticated, real-world architecture built on this principle. While dense, it provides a powerful blueprint for the kind of system thinking required.

Deterministic AI Orchestration: A Platform Architecture ... - Praetorian

This article describes a platform for autonomous software development that solves the problem of non-determinism by architecting a deterministic system around the LLM.

This is a detailed read, so focus on these specific sections to understand the core architectural ideas: Start with the Executive Summary to grasp the main thesis: treat the LLM as a non-deterministic kernel wrapped in a deterministic runtime. Next, read the section on the Orchestration Architecture, specifically the subsections "The Orchestrator-Worker Pattern" and "Coordinator vs. Executor Models." This explains how they enforce a separation of concerns between agents that plan and agents that execute code, preventing unpredictable behavior. Finally, and most importantly, read the section on The Runtime, focusing on "Deterministic Hooks" and the "Three-Level Loop System." This is the heart of the solution. It shows how they use lifecycle hooks—code that runs before and after the LLM acts—to enforce rules, validate outputs, and create deterministic feedback loops that the probabilistic LLM cannot bypass.

The Praetorian architecture provides several key takeaways for managing non-determinism:

  • Inversion of Control: Instead of a single, "smart" agent trying to do everything (a "Thick Agent"), the platform uses a "Fat Platform" to orchestrate many simple, stateless "Thin Agents." The intelligence and control reside in the deterministic platform, not the non-deterministic agents.
  • Separation of Duties: By physically preventing orchestrator agents from writing code and worker agents from delegating tasks, the architecture eliminates entire classes of unpredictable behavior.
  • Enforcement via Hooks: The system doesn't just ask the LLM to follow the rules in a prompt; it enforces the rules with deterministic code that intercepts the LLM's actions. This is the essence of building guardrails. For example, a hook can block an agent from committing code until a separate, independent testing agent has verified it.

This "deterministic orchestration" pattern is an advanced evolution of the "deterministic core / imperative shell." It shows how to build a reliable system not by changing the LLM, but by architecting a robust, deterministic harness around it.


Conclusion

In this lesson, we confronted the fundamental architectural shift introduced by AI: the move from a deterministic to a non-deterministic world. For decades, we have engineered systems to be as predictable as possible, and now we must learn to integrate components that are inherently probabilistic.

Key Takeaways:

  • Determinism is a Pillar of Engineering: The ability to produce the same output for the same input is the foundation of reliable testing, debugging, and the safe evolution of software.
  • AI is Inherently Non-Deterministic: LLMs break this fundamental assumption. Treating them as just another API or library is a flawed mental model that leads to fragile systems.
  • The Challenge is Containment, Not Elimination: The architectural goal is not to make AI deterministic but to build a deterministic system that contains and manages AI's variability. This involves shifting our focus from seeking "correctness" to ensuring "acceptability" within defined guardrails.
  • Patterns for Control: We can leverage patterns like the "deterministic core / imperative shell" and, more advanced, "deterministic orchestration." The key is to wrap the non-deterministic AI component within a deterministic runtime that enforces rules, validates outputs, and controls workflows through mechanisms like hooks and strict separation of duties.

Preview of the Next Lesson:

Having established the core challenge of non-determinism, we will next explore the specific non-functional requirements that AI components introduce. We'll examine practical concerns like model versioning, data privacy, the cost and latency of inference, and observability for AI systems. We will then describe common architectural patterns for integrating these services, such as the sidecar, gateway, and embedded model approaches.