skip to content

Before asserting that two published events were processed in order, how do you establish the scope in which that order is promised?

level: middleimportance: must knowfreq 58%

answer

  1. Order always carries a hidden qualifier
  2. In order with respect to what
  3. Find the routing value the design orders by
  4. Assert only over events sharing that value
  5. Unscoped order claims pass by luck

basics

~20 s

Order is promised only inside a scope — usually a routing value such as an account or order identifier, or a single producing path. Read the design to find that scope, then confine the assertion to events sharing it.

solid answer

~40 s

Start from the **contract**, never from a run. Ask what the transport and the consuming code together promise: almost always order holds only for events sharing one routing value — an account, an order, a device — and never across two different values or two independent producing paths. Then write that value into the case: produce every event the assertion covers under one key, filter every read by it, and name it in the case title and the failure text. Anything you want to say about events under different keys must be phrased as a *set* property, not a sequence. And check the promise survives the whole path: a transport can deliver in sequence while the consuming side still applies the effects concurrently. If nobody can state the scope, that is the finding.

code

pseudocode · 13 lines
pseudocode
key = new_unique_id()            # the one scope where order is promised

produce(type: "charged",   key: key, body: {...})
produce(type: "confirmed", key: key, body: {...})

wait_until stored_events(key).count == 2

observed = stored_events(key).map(e -> e.type)
assert observed == ["charged", "confirmed"],
       failure: "key=" + key + " observed=" + observed

# deliberately NOT asserted: any sequence relative to events
# produced under a different key by this run or any other

go deeper

for a junior

Be ready to say that asynchronous work completes in an order you do not control, and that a case asserting these arrived in the order I sent them is making a claim somebody has to justify from the design.

for a middle

Explain what an ordering scope is and how you find it: the routing value the design orders by, the producing path, and the point where the promise stops. Show the assertion confined to events carrying that value.

for a senior

An interviewer expects you to spot the over-claim in someone else's case and trace what would break it in a busy deployment — a second producing path, a shared identifier, an effect applied by two consumer processes — then rewrite the assertion down to what is actually promised.

for a principal

Own the position that ordering scope is a published contract between producing and consuming teams, stated once near the schema, rather than something each case rediscovers. Argue that an assertion exceeding the stated scope is a review defect.

## "In order" is never a complete claim Every ordering statement hides a qualifier: **in order with respect to what?** The honest forms name a scope — "events carrying the same account identifier are applied in the sequence they were accepted", or "one producing call site's own events reach the store in the sequence it emitted them". The dishonest form is the bare one: *the events arrive in order*. Read literally, that claims a single total sequence across everything the system emits, and very few designs offer such a thing. An automated case inherits the qualifier whether its author wrote one or not. A case that produces three events under three different identifiers and then asserts the stored rows appear in the production sequence has written a global-order assertion. It can pass for months against a quiet pre-production deployment and fail the first afternoon two consumer processes are running, or the first time one of the three events travels a different route. Nothing changed in the product; the assertion was always a claim about something nobody promised. ## Four questions that settle the scope All four are answerable from the design, before a single line of the case is written. 1. **What is the ordering key?** Nearly every asynchronous design that promises order at all promises it per routing value — an account, an order, a device, a tenant. That value *is* the scope. If the design names none, the honest reading is that order is not promised. 2. **Which producing path emits these events?** Two events emitted in sequence by one call site are a different claim from two events emitted by two independent services that merely happen to be related in the domain. 3. **Where does the promise stop?** A transport can hand events to a consumer in sequence and still leave the *effect* unordered, if the consuming side spreads work for one key across processes or writes results through a path with its own concurrency. Your assertion is usually about the effect, so the scope has to survive the whole path, not just the transport hop. 4. **What is explicitly unpromised?** Write that list down. It tells you which parts of the case must be phrased as set properties rather than as sequences. ## Claims and what they are actually worth | Assertion the case is tempted to make | What designs typically promise | Verdict | |---|---|---| | Three events under three identifiers land in production sequence | Nothing across identifiers | Reject outright | | Two events under one identifier are applied in sequence | Per-key order, where offered | Keep, scoped to that key | | Every event the run produced appears exactly once | Delivery, not sequence | Keep, as a set property | | The stored outcome equals the one the correct sequence produces | A convergent effect | Keep, and prefer it | The last row is the interesting one. A surprising number of requirements that arrive phrased as ordering — "the confirmation must come after the charge" — are fully satisfied by an assertion about the settled outcome, which holds under every sequence the design permits and therefore never argues with a legal interleaving. ## Put the scope in the case, not in your head - **Generate the ordering key per run.** A fixed identifier shared with another run turns two independent sequences into one interleaved sequence, and the assertion into noise. - **Produce everything the assertion covers under that one key.** If a step needs a second key, it belongs to a second assertion with its own scope. - **Filter every read by the key.** Reading "the latest three records" is a global-order assumption wearing a query. - **Name the key in the case name and in the failure text.** A failure that prints only "sequences differ" is one somebody has to re-run to understand. - **Assert over a projection** — the event types, or a sequence field — rather than over whole payloads, so the observed sequence is legible in the failure output. - **Say in a comment what you are deliberately not asserting.** The next reader will otherwise assume the omission was an oversight and "strengthen" the case. ## When nobody can tell you the scope That is a finding, not a blocked task. The ordering scope belongs to the contract between the team producing the events and the team consuming them. If neither can state it, the product carries an ordering assumption nobody owns, and your case would have been the first place it was ever written down. Record the ambiguity, write the case against the strongest property you can actually defend — the outcome invariant, or per-key sequence if any key exists — and take the scope question back to the owning team. What you must not do is adopt the boldest claim your one green run happens to support. That converts a documentation gap into an intermittent failure some weeks later, and the person debugging it at that point cannot tell whether the product regressed or the assertion was a guess from the beginning.

  • Your case produces its events under one identifier while another run works against the same deployment. Does that threaten the assertion?
    Not if the identifier is generated per run and every read is filtered by it. The danger is a fixed, shared value: two runs producing under the same identifier interleave into one sequence, and the assertion becomes noise that fails whenever the two runs overlap. Generating the ordering key per run is what keeps the scope private to the case.
  • How should the ordering scope show up in the failure output when the assertion breaks?
    Print the key, the observed sequence and the expected sequence, all three. A failure that reports only that two lists differ sends the reader back to reproduce the run before they can even tell which entity it concerned. The key is the filter someone will paste into a query five minutes later, so it belongs in the message, not only in the code.
  • The design promises order for one routing value, but the effect you assert on is written by several consumer processes. What changes?
    The scope you can assert shrinks to what survives the whole path. If work for one key can be applied concurrently, the sequence promise stops before the stored result, so a sequence assertion on that result is over-claiming. Assert an outcome invariant instead, and record that the promise does not reach the observable you are reading.

Two lanes of traffic each keep their own queue. Nothing promises the third car in one lane clears the light before the third car in the other, however many times you have watched it happen.

saying these in an interview costs you the question

  • Assumes everything the system emits shares one global sequence
  • Asserts arrival order without naming the value order is scoped to
  • Treats a green run as proof that ordering is guaranteed
  • Produces events under different identifiers, then asserts one sequence
  • Says order is safe because only one consumer instance runs today