Deterministic Core, Non-Deterministic Shell Emerges as New Design Guide
New design guideline puts determinism at the core instead of pure functions, focusing on testability via state machines.
A design guideline centered on determinism has been presented. An article on outdata.net, introduced on Lobsters, discusses a proposal to reframe the functional core and imperative shell. It is an essay dated August 3, 2026, and was shared on September 20, 2026. The title is Deterministic Core, Non-Deterministic Shell. The author is recorded as outdata.net via teymour. The aim is to ensure testability and expand applicability.
Design Philosophy Isolating Business Logic
in the Core
The starting point is an idea proposed by Gary Bernhardt 14 years ago. Its name is Functional Core, Imperative Shell. It was not entirely new in computer science, but its clarity of expression was highly praised. It has been widely used as a foundation for discussing testing and determinism. In the Lobsters report on outdata.net via teymour, the approach of dividing code in two is整理 once again.
Functional Core, Imperative Shell architecture divides the code into two parts.
The core consists of pure functions. It has no I/O and performs no destructive state updates. It is positioned to handle only business decision logic. The shell holds state and coordinates external dependencies. It has relatively few branches and mostly linear execution. It acts on the outside world based on values received from the core. Writes to storage, sending requests, and screen updates fall into this category.
The core makes decisions, and the shell coordinates dependencies. The core has many branches, and the shell handles integration. The core is isolated from the outside world, and the shell is coupled to it. This contrast lies at the heart of the design. The core returns the same result for the same input. No mocks or replacements are needed, allowing complex business decisions to be verified directly. It is a structure in which tests reveal much of the system’s behavior.
Why Determinism Enables Testability
The reason pure functions suit testing lies in determinism. If the input stream is the same, the output stream is the same. The key point is that reproducibility of behavior is guaranteed. Combined with isolation, this lowers verification costs. The effect is greatest for business decisions with complex branching. The article states that determinism is not unique to functional programming. Its position is that a change of perspective offers another path to the same goal. A flow of values and a sequence of assignments can be interpreted as different expressions of the same thing. This is the basis for the claim that state machines provide equivalent benefits.
The structure in which the shell handles contact with the outside world also overlaps with separation of operational delegation. The permission separation covered in Desktop Commander MCP Delegates Terminal Operations to AI offers hints for designing shell responsibilities. The stance of separating decision from execution also applies to infrastructure design. The separation of reasoning and execution shown in Infinigence Strategy for Agentic Infra Chosen by Four Companies is also a useful reference. Handling uncertainty at low levels is also a challenge in the driver layer. The enablement decision shown in Mesa Rusticl Enables Mali Panfrost by Default also illustrates the difficulty of setting boundaries.
Verifying the Equivalence of Pure Functions
and State Machines
An implementation of addition is contrasted as a concrete example. One is reduction by a pure function. The other is transition by a machine with internal state. The article states that both share determinism. Its argument is that being imperative does not compromise determinism.
The function example takes the following form. It takes an input sequence and returns the sum from an initial value of 0. It has no side effects and is easy to reason about. The machine example adds numbers to private state. A sequence of transition calls determines the state. A getter returns the current total. Given the same transitions in the same order, it reaches the same state. Given 1, 2, 3 in that order, the result is 6. The function version and the machine version agree.
This equivalence broadens design freedom. The view is that it is enough to relax purity and require determinism. This preserves testability while increasing implementation options. It matters where language or performance constraints are strong. Thoroughgoing pure functional style in C is not always realistic. The article explicitly notes that difficulty. Relaxing the requirement to determinism is seen not as a practical compromise but as an extension. It could be called a proactive step to preserve a verifiable core.
Extensibility Applicable Even in Imperative
Languages
Pure functional style is an excellent norm. But language or performance considerations can make it hard to adopt. Embedded systems, low-level code, and reworking existing assets are typical cases. Writing everything with immutable values and pure functions is a heavy burden. Hence the proposal to replace the requirement with determinism. Put Deterministic Core in place of Functional Core. Put Non-Deterministic Shell in place of Imperative Shell. The title comes from this substitution.
The deterministic core puts reproducibility at its center. Once the input sequence and transition order are fixed, the result is fixed. The dividing line is not whether side effects exist but whether reproduction is possible. The non-deterministic shell takes on uncertainty. Time, random numbers, communication delays, and user actions fall into this category. It consolidates interaction with the outside world in one place. It is an approach that protects the reproducibility of the core rather than its purity. It has the advantage of easier application to existing systems. Work can start by redrawing boundaries without a full rewrite.
Consolidating External Dependencies and
Directions for Practical Use
The shell is responsible for coordinating dependencies and maintaining state. Its basic operation is to query the core and deliver the results. The core returns values as black-box decisions. The shell converts those values into effects on the outside world. Recording, sending, and rendering are representative examples. The shell’s paths are linear and easy to follow. Discipline is required not to bring complex branching into the shell. Push branching to the core and keep the shell to procedures. This division clarifies the scope of testing.
In practice, boundary design determines success or failure. Placing time acquisition and random number generation in the shell is natural. Communication and persistence should also be consolidated on the shell side. The core should be shaped to receive only values and transitions. A state machine’s transition function can remain in the core. Recorded transition order provides material for reproduction tests. Replaying the recorded order reproduces defects. There is the advantage of verifying through history replay without relying on mocking mechanisms. In concurrent processing, fixing the order becomes an additional challenge. Separating concerns by ordering in the shell and deciding in the core is seen as effective.
The article’s argument also leads to rescuing existing assets. Even code that is difficult to purify can be targeted for determinism. Make state updates explicit as transitions and keep a history. Move external effects to the shell and close the core around values. The possibility of gradual restructuring is practical. Tests focus on the core, while the shell is limited to integration checks. It is judged capable of building a cost-effective verification system.
Editorial Opinion
Over the next 3 to 6 months, we expect test design to be revisited. The technique of carving out the core as deterministic is easy to adopt as a guide for restructuring existing code. In particular, introducing state machines is likely to encourage application to assets where functionalization is difficult.
In 1 to 3 years, it could become established as a language-independent testing norm. We expect moves to state determinism guarantees explicitly in design documents to spread. A culture of separating the uncertainty of the execution environment from the certainty of business decisions will take root.
The remaining question is where to place the boundary of determinism. The scope of state and the handling of time and randomness are not self-evident. How to record and replay nondeterminism that leaked into the shell is also at issue. Who will verify the validity of the boundary.
References
- “Deterministic Core, Non-Deterministic Shell”, by outdata.net via teymour — Lobsters, 2026-09-20T21:13:51.000Z (ARR)
- Source URL: https://outdata.net/blog/260803
Frequently Asked Questions
- What is Functional Core, Imperative Shell?
- It is a design that splits code into a pure core handling business decisions and an imperative shell interacting with the outside world. The core has no I/O and is highly reproducible. The shell focuses on holding state and coordinating external dependencies. The aim is to clarify the scope of testing.
- How is Deterministic Core, Non-Deterministic Shell different?
- It differs in relaxing the requirement for purity to a requirement for determinism. State machines can also be included in the core. Imperative implementations are allowed if reproducibility holds. It is easier to apply where language or performance constraints are strong.
- Why can a state machine be called deterministic?
- Because giving the same transition order leads to the same state. In the addition example, transitioning with 1, 2, 3 in that order gives 6. The result matches reduction by a pure function. History replay can be used for reproduction tests.
Comments