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 randomnessnow(), 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:

  1. Deterministic by default (P01). Same input, same output, always.
  2. 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.
  3. No hidden state. Values are immutable; equality is structural, not referential.
  4. Recorded model calls. AI-assisted steps replay from recorded cassettes, so model calls fall inside the determinism guarantee rather than outside it.
  5. 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.

DeterminismFormal verification
Question answeredDoes it behave the same every time?Does it satisfy a specification?
CostStructural disciplineSpecification and proof effort
Failure modeDivergence, detectable by re-runUnproven property, detectable by proof failure
Scales toWhole systemsTargeted 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:

  1. Run the same job twice and diff the output byte-for-byte, not "spot-check it".
  2. Run it on a different machine and different OS, and diff again.
  3. Re-run last month's recorded job today and compare digests.
  4. 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.

Next