What Is Deterministic Event Ordering and Why a Replay Should Give the Same Answer
Deterministic event ordering means that the same set of events always comes out in the same sequence, no matter the order they arrived in or how many times each one arrived. Feed the events in once and you get a sequence. Feed them in again next week, shuffled, with half of them sent twice, and you get the same sequence, byte for byte.
That sounds like it should be the default. It almost never is. Most systems process events in arrival order, and arrival order is an accident of networks, retries and clocks.

Why Arrival Order Isn’t Real Order
Three ordinary things scramble events on their way in.
Retries. A webhook provider that didn’t get a fast enough reply sends the event again. A producer that lost its connection resends its last batch. The same event now exists twice, and if you count it twice, your totals are wrong.
Clocks. Two servers never agree exactly on the time. If the checkout service’s clock runs a few milliseconds ahead of the payments service’s, a payment can be stamped before the order it pays for. Sort by timestamp and the payment comes first, which is impossible.
Paths. Events from different machines take different routes. Partitions in a log are read at different speeds. A slow consumer catches up in a burst. Tick 88 of a price feed arrives before tick 87.
The Usual Fix, and Where It Breaks
Most teams handle this with a hand-made buffer: keep recent events in memory for a few seconds, sort them, drop the ones you’ve seen, and pass them on. It works most days.
The trouble starts with the question nobody asked when it was written: what happens to an event that shows up after its few seconds are over? Usually it gets passed on wherever it lands. That one decision makes the output depend on timing. Run the same day’s events again, faster or slower, and the late ones land somewhere else. The totals differ slightly, the audit doesn’t match, and nobody can say why.
The Three Rules That Make It Repeatable
Deterministic ordering comes down to deciding those edge cases ahead of time and writing them down.
A bounded window with a hard edge. Events are held until the newest time seen has moved a set distance past them. An event that arrives after that is refused, and reported, but never slipped in. Refusing it is what keeps everything else repeatable.
One fixed order. Time first, then a tie-breaker that never changes, such as a logical clock counter, then producer, then sequence number. Any two events have exactly one right order.
Identity for repeats and parents. Every event has an identity, so a repeat is recognised and dropped. An event can name its parents, the events that must come before it, and it waits for them for a bounded time even when its own clock disagrees.
With those three rules, the sequence is a fixed function of the events. A hash of the output becomes a fingerprint of the data, so two runs can be compared in one line.
Where It Matters
- Replays and backfills. Re-running yesterday’s events should give yesterday’s answer.
- Audits. A regulator or a customer asks how a number was reached. You can show the exact sequence, and prove it’s the one you used.
- Redundant feeds. Market data and telemetry often arrive twice, over two lines, each missing a little. Merging them in a fixed order gives one complete stream with real gaps reported.
- Two systems that must agree. A billing system and a ledger that both consume the same events reach the same state only if they see them in the same order.
How Inorderly Does It
Inorderly is these rules as a small Go library and a command that reads and writes NDJSON. You set the window. It holds events, drops repeats, waits for parents, refuses the late and reports them, and prints the SHA-256 of what it wrote. The quick start shows the same events in three different orders giving one hash, and the contract on GitHub states every rule exactly.