Cron, queues and status columns all make you store where you got to. A durable execution engine journals every step and, when a worker dies, replays that journal to restore the exact in-memory position — so await sleep(three days) really waits three days across restarts. The price is determinism: no clock, no randomness, no network calls inside workflow code, and that constraint is most of the learning curve.
Half an hour, and the point is to see the mechanism rather than read about it. Start the local dev server, write one workflow that loops with a sleep, run it with the sleep set to ten seconds instead of a day, then kill the worker mid-run and start it again — and watch it continue rather than restart. Then open the event history for that run in the UI, because that is the moment the tool stops being magic: resume is not a feature, it is a log being replayed. Two traps are worth hitting deliberately rather than in production. Put a Date.now() inside the workflow and watch replay diverge silently; that is why side effects belong in activities. And change the workflow code while a run is in flight, which is what versioning gates exist for. Details here — pricing tiers, the funding round, the agent-framework integrations — come from a 2026-08-28 reading and should be confirmed before anyone quotes them.
Why
The category name hides the product. "Workflow engine" sounds like scheduling, and scheduling is the part you already have — cron fires, a queue delivers, a status column remembers. What none of those give you is the ability to be halfway through a multi-step process and survive the machine dying. Every team eventually builds that themselves, and what they build is a hand-rolled state machine spread across a status enum, a retries column and a set of guard clauses. That code is where the bugs live, because it encodes a resumable process in a language that has no way to express one.
Determinism is not a quirk, it is the whole bargain. Replay only works if the same history always drives the same path, which is why a clock read or a random number inside workflow code breaks it — and breaks it silently, on recovery, long after the change was made. Pushing side effects into activities is not ceremony; it is the line between "the part that can be replayed" and "the part that must not be." A team that understands that one distinction has most of the tool. A team that does not will fight it for a month.
And the reason this category got loud in 2026 is agents, not microservices. An agent run is long, waits on humans, calls tools in sequence, and must survive a crash with its tool history intact — which is a precise restatement of what durable execution already solved. That is worth noticing beyond this one vendor: when a new workload turns out to need an old primitive, the old primitive gets repriced rather than reinvented. For settlement flows the fit is just as direct — close, wait for the oracle, dispute window, settle, withdraw is long-running with external waits at every step — with one caveat that is not optional: on-chain transactions must be idempotent under retry. Fix the nonce or the request id outside the retried activity, or the engine's most useful feature quietly becomes double-spending. The honest comparison is that this is the most powerful option in its class and also the heaviest; if the need is one nightly job, a lighter event-driven runner answers it sooner, and the reason to take the heavy one is that the same engine will carry the settlement flow later.
How it works
What each tool actually stores
Tool
Who remembers progress
Survives a mid-run crash
cron
you, in a status column
no — it starts over
queue
you, per message
partly — at message granularity
hand-rolled state machine
you, in enums and guards
only where you coded it
durable execution
the engine, as an event history
yes — replayed to the exact step
The bargain, in one line each
You get: await sleep('3 days') that means it; retries declared instead of coded; a run you can inspect step by step.
You give: determinism — no Date.now(), no Math.random(), no direct network calls inside workflow code.
The seam: everything non-deterministic moves into an activity, which is the replayable/not-replayable boundary.
Two traps, both better hit locally
A clock inside the workflow. Replay takes a different branch than the original run and fails quietly, on recovery — the worst possible time.
Deploying new workflow code while runs are in flight. The history no longer matches the code path; versioning gates exist for exactly this and belong in from day one.
On-chain settlement has one extra rule
Retry is the feature, so the transaction must be idempotent under it. Fix the nonce or request id outside the retried activity. Otherwise the engine's best property turns a transient RPC error into a double send.
Choosing weight honestly
Need
Reasonable answer
one nightly job that must resume
a light event-driven runner gets you there sooner
settlement: close → oracle → dispute → settle → withdraw
the heavy engine, because the flow is genuinely long-running
"we might need both"
pick the one you will still want at the second use case
cron·큐·상태 컬럼은 전부 어디까지 했는지를 개발자가 저장하게 만듭니다. 지속 실행(durable execution) 엔진은 모든 단계를 저널에 남기고, 워커가 죽으면 그 저널을 재생해 메모리 상태를 정확히 그 지점으로 복원합니다 — 그래서 await sleep(3일) 이 재시작을 몇 번 겪어도 진짜로 3일을 기다립니다. 대가는 결정성입니다 — 워크플로 코드 안에서 시계·난수·네트워크 호출 금지, 그리고 그 제약이 학습 곡선의 거의 전부입니다.
반 시간이고, 목적은 읽는 게 아니라 기계장치를 눈으로 보는 것입니다. 로컬 개발 서버를 띄우고, sleep 이 들어간 루프 워크플로 하나를 만들되 하루가 아니라 10초로 두고 돌립니다. 그다음 도중에 워커를 죽였다가 다시 켜서 — 처음부터가 아니라 이어서 도는 것을 봅니다. 그리고 UI 에서 그 실행의 이벤트 히스토리를 엽니다. 거기서 이 도구가 마법이기를 그만둡니다 — 재개는 기능이 아니라 재생되는 로그입니다. 함정 둘은 프로덕션이 아니라 여기서 일부러 밟아 보십시오. 워크플로 안에 Date.now() 를 넣고 재생 때 경로가 조용히 갈라지는 것을 봅니다 — 부수효과가 액티비티로 가야 하는 이유입니다. 그리고 실행 중인 워크플로의 코드를 바꿔 봅니다 — 버전 게이팅이 존재하는 이유입니다. 여기의 세부(요금제, 투자 라운드, 에이전트 프레임워크 통합)는 2026-08-28 기준 독해이므로 인용 전에 확인하십시오.
왜
범주 이름이 제품을 가립니다."워크플로 엔진" 은 스케줄링처럼 들리는데, 스케줄링은 이미 갖고 있는 부분입니다 — cron 이 쏘고, 큐가 배달하고, 상태 컬럼이 기억합니다. 그중 어느 것도 주지 않는 것은, 여러 단계짜리 과정의 한복판에 있다가 기계가 죽어도 살아남는 능력입니다. 결국 모든 팀이 그걸 직접 만들고, 만들어지는 것은 상태 enum·재시도 컬럼·가드 절에 흩어진 수제 상태 머신입니다. 그 코드가 버그의 서식지입니다 — 재개 가능한 과정을, 그것을 표현할 방법이 없는 언어로 적어 놓았기 때문입니다.
결정성은 특이한 규칙이 아니라 거래 조건 그 자체입니다. 재생은 같은 히스토리가 항상 같은 경로를 태울 때만 성립하고, 그래서 워크플로 코드 안의 시계 읽기나 난수가 그것을 깹니다 — 그것도 조용히, 복구 시점에, 바꾼 지 한참 뒤에. 부수효과를 액티비티로 미는 것은 격식이 아니라"재생될 수 있는 부분" 과 "재생되면 안 되는 부분"사이의 선입니다. 그 구분 하나를 이해한 팀은 이 도구의 대부분을 이해한 것이고, 못 한 팀은 한 달을 싸웁니다.
그리고 2026년에 이 범주가 시끄러워진 이유는 마이크로서비스가 아니라 에이전트입니다. 에이전트 실행은 길고, 사람을 기다리고, 도구를 순서대로 부르고, 도구 호출 히스토리를 지닌 채 크래시에서 살아남아야 합니다 — 지속 실행이 이미 풀어 둔 문제의 정확한 재진술입니다. 이건 이 벤더 하나를 넘어 알아 둘 값이 있습니다 — 새 작업 부하가 옛 원시 기능을 필요로 한다고 밝혀지면, 그 원시 기능은 재발명되는 게 아니라 값이 다시 매겨집니다. 정산 흐름도 들어맞음이 똑같이 직접적입니다 — 마감 → 오라클 확정 대기 → 분쟁 창 → 정산 → 출금 은 매 단계에 외부 대기가 있는 장기 실행입니다 — 다만 선택이 아닌 단서 하나: 온체인 트랜잭션은 재시도 아래에서 멱등해야 합니다.nonce 나 요청 id 를 재시도되는 액티비티 바깥에서 고정하십시오. 안 그러면 이 엔진의 가장 쓸모 있는 기능이 조용히 이중 전송이 됩니다. 정직한 비교는 같은 급에서 가장 강력하고 동시에 가장 무겁다는 것입니다 — 필요한 게 야간 작업 하나뿐이라면 더 가벼운 이벤트 러너가 더 빨리 답하고, 무거운 쪽을 고르는 이유는 나중에 같은 엔진이 정산 흐름까지 태울 것이기 때문입니다.
동작 방식
각 도구가 실제로 저장하는 것
도구
진행 상황을 기억하는 주체
도중 크래시 생존
cron
개발자 — 상태 컬럼에
아니오 — 처음부터
큐
개발자 — 메시지 단위로
부분적 — 메시지 입도까지
수제 상태 머신
개발자 — enum·가드 절에
코딩한 곳까지만
지속 실행
엔진 — 이벤트 히스토리로
예 — 정확히 그 단계로 재생
거래 조건, 한 줄씩
얻는 것: 진짜로 기다리는 await sleep('3 days'), 코드가 아니라 선언으로 끝나는 재시도, 단계별로 들여다볼 수 있는 실행.
주는 것: 결정성 — 워크플로 코드 안에서 Date.now()·Math.random()·직접 네트워크 호출 금지.
경계선: 비결정적인 것은 전부 액티비티로 — 재생 가능/불가능의 경계가 그것입니다.
함정 둘, 둘 다 로컬에서 먼저 밟을 것
워크플로 안의 시계. 재생이 원래 실행과 다른 분기를 타고 조용히, 복구 시점에 깨집니다 — 가능한 최악의 시점입니다.
실행 중에 워크플로 코드 배포. 히스토리와 코드 경로가 어긋납니다 — 버전 게이팅이 정확히 이걸 위해 있고, 첫날부터 써야 합니다.
온체인 정산에는 규칙이 하나 더
재시도가 곧 기능이므로, 트랜잭션이 그 아래에서 멱등해야 합니다. nonce·요청 id 를 재시도되는 액티비티 바깥에서 고정하십시오. 안 그러면 일시적 RPC 오류가 이중 전송이 됩니다.