What is deterministic software?
Software is deterministic when the same input always produces the same output — not usually, not to within a tolerance, but exactly, on every run, on every machine, forever.
Formally, for every function f in the system:
∀ x: f(x) = f(x)
That looks trivial. Almost no modern system satisfies it.
Why most systems are not deterministic
Non-determinism usually enters by accident, through:
- Ambient time and randomness —
now(),Math.random(), unseeded UUIDs. - Iteration order — hash maps and sets whose order varies between runs or versions.
- Concurrency — interleaving that changes with scheduler load.
- Hidden mutable state — globals, caches, singletons, lazily initialised fields.
- Floating-point drift — differing precision or instruction selection across platforms.
- Language models — sampling from a distribution, with output that changes between calls.
Each is individually manageable. Together they mean that "it worked yesterday" is a statement about luck, not about the system.
Determinism is a security property, not a feature
The usual argument for determinism is convenience: reproducible builds, stable tests, easier debugging. That undersells it. In regulated work, determinism is what makes a claim checkable.
- Reproducibility. A result you cannot reproduce is not evidence.
- Auditability. An auditor can re-run the pipeline and compare digests instead of trusting a narrative.
- Attribution. When output changes, the cause is a change in input or in code — never in the weather.
- Tamper-evidence. If the recorded digest and the re-run digest disagree, something is wrong, and you know it immediately.
This is why "the model is usually right" is not an acceptable basis for transforming a payments system, and why determinism is treated in daitai as a property of the platform rather than a nice-to-have.
How determinism is enforced in daitai
Determinism does not survive as a convention. It has to be enforced structurally:
- Deterministic by default (P01). Same input, same output, always.
- Explicit non-determinism (P02). When randomness is genuinely required, it is marked
@nondeterministic, typed, and isolated — visible in the signature rather than hidden in the body. - No hidden state. Values are immutable; equality is structural, not referential.
- Recorded model calls. AI-assisted steps replay from recorded cassettes, so model calls fall inside the determinism guarantee rather than outside it.
- Blocking CI gates. Algebraic invariants are checked on every build. A broken law fails the build.
The measured result of that enforcement: two independent 500-iteration passes producing a byte-identical event stream with zero divergences, 20 of 20 algebraic invariants passing as a blocking gate, and byte-identical stdout against JDK 17 on the Java parity suite. The artefacts are on the Proof page.
Determinism vs formal verification
They are complements, not substitutes.
| Determinism | Formal verification | |
|---|---|---|
| Question answered | Does it behave the same every time? | Does it satisfy a specification? |
| Cost | Structural discipline | Specification and proof effort |
| Failure mode | Divergence, detectable by re-run | Unproven property, detectable by proof failure |
| Scales to | Whole systems | Targeted components |
Determinism is the cheaper property and the prerequisite. Without it, a proof about one run tells you nothing about the next. See What is formal verification? for the other half.
How to tell whether your own system is deterministic
A practical test, in order of increasing severity:
- Run the same job twice and diff the output byte-for-byte, not "spot-check it".
- Run it on a different machine and different OS, and diff again.
- Re-run last month's recorded job today and compare digests.
- Shuffle input ordering where order is not semantically meaningful and confirm the output is unchanged.
Any diff is a finding. Most teams discover on step one that they have never actually tried.