Build a workflow engine (Temporal / Airflow / Cadence style) (13 scenes)
Scene 02 · The event history is the source of truth
Stop trusting RAM: append every step's result to a durable, append-only event history, so a crash that wipes memory leaves the record of what already happened intact.
Previously
The double-charge came from trusting RAM; the fix is to append every step's result to a durable history that a crash can't wipe. But a saved record is useless on its own — we still need to get the half-finished ORDER #1001 back into the exact state it was in. How does the engine turn a log of events back into a running function?
Scene 02
The event history is the source of truth
Diagram
TOP panel: the in-RAM variables from scene 1 — wiped to blank the instant the crash hits, because a plain function's progress only ever lived in memory. BOTTOM strip: the event history — an append-only log on disk where each completed step adds one event like "ChargeCard completed=ok $42". The skull touches only the top panel. "Event sourcing" means current state isn't stored directly; it's DERIVED by folding the events left-to-right — the log is truth, the variables are a throwaway cache, like a bank ledger versus a sticky-note balance.
Scene 1's bug was that ORDER #1001's progress lived only in RAM, so the crash erased it. Watch the fix. As each step finishes, the engine writes one fact to a brand-new strip on disk: not a variable it can overwrite, but an event APPENDED to the end — WorkflowStarted, then ChargeCard scheduled, then ChargeCard completed=ok $42. The top RAM panel still mirrors the same progress as ordinary variables. That bottom strip is the **event history**: a durable, append-only log on disk that is only ever added to, never overwritten. Now drop the same crash. The RAM panel blanks exactly like before — but the event history is untouched. The lost sticky note vs the bank ledger: the record that ChargeCard already ran survives.
Implementation
Engine.recordStep
each finished step APPENDS one event — never overwrites
1def recordStep(activity, result):2 event = ActivityCompleted(activity, result)3 history.append(event) # to disk, append-only4 # the RAM cache is a convenience, not the truth5 ram[activity] = result6 return event
Engine.currentState
where are we now — derived, never stored
1def currentState():2 state = {}3 for event in history.readFromStart():4 state = apply(state, event) # fold left-to-right5 return state # rebuilt, not saved
Engine.afterCrash
what survives under each store
1def afterCrash():2 ram = {} # RAM is wiped3 if store == 'ram-variables':4 return ram # nothing survived5 # event sourcing: re-derive from the durable log6 return currentState() # ChargeCard completed=ok
Not sure what to ask? Tap a question — the staff engineer answers in the chat panel.