Versioning: the year-later replay — getVersion gates for in-flight executions
Editing a live workflow's code makes an in-flight execution replay against a different step sequence than its recorded history, diverging into a non-determinism error — so the safe path is a version gate that routes old executions down the old path and new ones down the new path, both deterministic.
A version gate lets ORDER #1001, started a year ago under v1, wake and replay safely alongside v2 executions. You now hold the full toolkit: history, replay, activities, workers, retries, idempotency, timers, signals, sagas, children, versioning. The last question isn't a new mechanism — it's judgment: for a real workload, is this code-as-workflow replay model even the right tool, or is a DAG scheduler or a state machine the better fit?
Scene 11
Versioning: the year-later replay
- Watch
- Try it
- Predict
- Capture
It's a year later. ORDER #1001 charged the card $42, then went to sleep on its 30-day timer — and it's STILL alive, waiting to wake. Today you ship v2 of the order workflow: it inserts a FraudCheck between ChargeCard and ReserveInventory. Here's the trap you can't see in a normal service. There is only ONE deployed codebase now — v2 — and BOTH executions replay through it. But #1001's recorded history was written under v1: it never has a FraudCheck event, because that step didn't exist when it ran. A run like #1001 — started under older code and still alive, carrying expectations frozen into its history — is an in-flight execution. #1002, started today, ran against v2 from its first step, so its history already carries FraudCheck. Watch both executions appear on the strip: same code ahead of them, but two different histories behind them.
Highlighted lines are the ones running in the diagram right now.
def replay(execution):history = execution.history # frozen, append-onlycursor = Cursor(history)for cmd in run_workflow(execution):recorded = cursor.next_event()if recorded is None:return execute_live(cmd) # caught upif cmd.kind != recorded.kind:raise NonDeterminismError(cmd, recorded)feed_back(cmd, recorded.result) # don't re-run
def order_workflow(order):charge_card(order, 42)v = get_version('fraud', min=1, max=2)if v >= 2:fraud_check(order)reserve_inventory(order)ship(order); send_email(order)
def get_version(change_id, min, max):marker = history.find(change_id)if marker is not None:return marker.version # what this run committed to# first time: record max so future replays agreerecord(VersionMarker(change_id, max))return max
Where this sits in Build a workflow engine (Temporal / Airflow / Cadence style)
Scene 11 of 13, in the Evolve & ship act — Version safely, then choose the right engine.. A year-old in-flight execution can't be hot-fixed; a version gate routes old executions down the old path and new ones down the new path, so both replay deterministically.
Up next. You've earned every mechanism. The capstone isn't a new one — it's choosing the right tool. The same ORDER #1001 can be expressed as Temporal-style code, an Airflow DAG, or a Step Functions state machine. Each fits a different shape of workload, and seeing the 30-day wait and the cancel signal go awkward in a DAG is the cleanest proof of what durable execution is actually for.
All 13 scenes in Build a workflow engine (Temporal / Airflow / Cadence style) · Every curriculum