Home Resume
Home Blog AWS Architecture Series #17 — Step Functions Standard vs Express: The Type You Cannot Change Later…
AWS AWS Architecture Series

AWS Architecture Series #17 — Step Functions Standard vs Express: The Type You Cannot Change Later

Standard is exactly-once, Express is at-least-once, and synchronous Express is at-most-once — which means it can end up not running at all. The workflow type is fixed when the state machine is created and cannot be updated.

Verified against current vendor documentation on 12 August 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

An order-processing workflow runs about a million times a month. Ten states each: validate, reserve stock, charge the card, write the ledger entry, emit an event, and so on. The Step Functions line on the bill is around $250, and somebody notices that Express Workflows would do the same million runs for roughly a dollar.

That is not a rounding error, and the instinct to move is correct arithmetic. The team switched. Within a fortnight they had duplicate ledger entries, a payment charged twice, and a handful of orders where a step appeared to have simply not happened.

Nothing was misconfigured. Express Workflows were doing exactly what AWS documents them to do. The mistake was treating the two types as the same product at different price points, when the price difference is the difference in guarantees.

1Standard is exactly-once. Express is not.

Standard Workflows follow an exactly-once model, which AWS describes as tasks and states never running more than once unless you specify Retry. That is what makes them suitable for non-idempotent actions — the documentation names starting an EMR cluster and processing payments specifically.

Asynchronous Express Workflows use an at-least-once model, so an execution could potentially run more than once. Charging a card is the textbook example of something that must not.

Fix

Ask whether every step is safe to repeat before looking at the price. If any step is not, the answer is Standard.

2Synchronous Express is at-most-once, which is worse than it sounds

The synchronous variant looks like the safe middle ground: it waits for completion and returns the result, so it feels transactional. Its guarantee is at-most-once — and AWS is explicit that workflows don't restart if an exception occurs.

At-least-once fails loudly: you get duplicates, and duplicates are visible. At-most-once fails silently: the work does not happen, the caller gets an error, and unless something reconciles afterwards nobody finds out. Those are opposite failure modes and they need opposite compensating designs.

Fix

Treat synchronous Express as best-effort. If the caller cannot safely retry the whole thing, it is the wrong tool.

3Express drops the integration patterns that make orchestration worth having

Express Workflows do not support Job-run (.sync) or Callback (.waitForTaskToken) service integration patterns. The first is how you wait for an EMR job or a Glue crawler to actually finish. The second is how you pause for a human approval or an external callback.

Distributed Map and Activities are unsupported too. So the migration is frequently blocked before semantics even come up: the workflow uses a pattern Express does not have.

Fix

Check the state machine definition for .sync, .waitForTaskToken, Distributed Map and Activities first. It is a five-minute check that ends most of these discussions.

4The type cannot be changed afterwards

AWS states it plainly: the workflow type can not be updated after you create a state machine. There is no conversion, no toggle, no migration path.

Changing your mind means creating a new state machine, which means a new ARN, which means updating every EventBridge rule, API Gateway integration, IAM policy and parent workflow that referenced the old one. That is a deployment, not a setting — and it is why this choice deserves more thought at creation time than it usually gets.

Fix

Decide the type when the workflow is designed, not when the bill arrives. Treat it as immutable infrastructure, because it is.

Architecture

Diagram: Standard workflows are exactly-once and run up to a year; asynchronous Express is at-least-once; synchronous Express is at-most-once and does not restart on exception. Both Express types are capped at five minutes and do not persist state between transitions. The workflow type cannot be changed after creation.

Three guarantees, not two options

The comparison is usually framed as Standard versus Express, which hides the fact that there are three distinct guarantees on offer. Standard is exactly-once. Asynchronous Express is at-least-once. Synchronous Express is at-most-once. Each one demands something different from the code downstream.

Exactly-once asks nothing: write the workflow, run it. At-least-once requires every step to be idempotent — AWS gives the example of a DynamoDB PUT, which is safe to repeat because it overwrites rather than accumulates. At-most-once requires a reconciliation path, because the failure mode is work that quietly did not happen.

Why Standard can afford to be exactly-once

The mechanism is stated directly in the docs and it explains both the price and the limits: execution state internally persists between state transitions in Standard, and does not persist in either Express type.

Persisting state after every step is what allows a workflow to run for a year, survive a failure mid-flight, be inspected step by step 89 days later, and return an idempotent response when started twice with the same name. It is also why Standard is billed per state transition: each transition is a durable write, and that is the unit of work being sold.

Express does not persist, so it cannot offer any of that — and correspondingly bills for the run rather than the steps. The two pricing models are not two prices for one thing. They are prices for two different things.

The five-minute wall, and a console trap behind it

Express Workflows run for five minutes, maximum, against Standard's one year. That gap is large enough that most workflows are not near the boundary and the decision is obvious. The ones that are near it are the dangerous ones, because a workflow that fits in five minutes today can grow.

There is also a trap worth knowing when testing: if you run a synchronous Express workflow from the console, the StartSyncExecution request expires after 60 seconds. To get the full five minutes you have to call it from the SDK or CLI. A workflow that looks like it times out at one minute in the console may be perfectly healthy.

Why This Architecture Holds Up

The cost gap is real, and it is the point

Take the million executions of a ten-state workflow. Standard bills every transition: 10,000,000 × $0.000025 = $250. Express bills the run — $1.00 per million requests plus duration at $0.0600 per GB-hour, which at 100 ms and 64 MB of memory works out under twenty cents. Call it $1.20 against $250.

That ratio is not evidence that Standard is overpriced. It is the price of durable state after every step, and it scales with the number of steps precisely because that is what is being stored. The useful consequence is that Standard's cost is driven by state count, not execution time — so a long, cheap workflow with few states can be inexpensive, while a fast workflow with forty states is not.

Idempotency is the question, and it is not free either

“Make everything idempotent and use Express” is a legitimate strategy, but it is engineering work, not a configuration change. It usually means threading an idempotency key through every downstream call, storing seen keys somewhere with a TTL, and handling the partial-failure cases where a key was recorded but the work did not complete.

Compare that honestly against $250 a month before committing. For a payments workflow at this volume, Standard is almost certainly cheaper than the engineering time to make the alternative safe — and considerably cheaper than getting it wrong once.

Observability is opt-in on Express, and absent by default

Standard executions can be listed and described through the Step Functions API and debugged visually in the console, with history retained for 90 days (reducible to 30 by quota request, if a compliance regime requires it).

For Express, execution history is not captured by Step Functions — logging must be enabled through CloudWatch Logs. If you migrate and do not turn that on, you have swapped a queryable 90-day history for nothing at all, and you will discover this while investigating the first duplicate.

Key Architecture Decisions

SituationChooseBecause
Payments, ledger writes, cluster provisioning Standard Non-idempotent actions need exactly-once. AWS names these cases directly.
High-volume ingestion, transform-and-store Express, asynchronous At-least-once is safe when every step overwrites rather than accumulates.
Request/response behind API Gateway Express, synchronous Returns the result to the caller — but at-most-once, so the caller must be able to retry.
Waiting on EMR, Glue, or a human approval Standard, no choice .sync and .waitForTaskToken are unsupported in Express.
Large-scale parallel fan-out Standard Distributed Map is not supported in Express.
Anything running beyond five minutes Standard Five minutes is a hard ceiling, and workflows tend to grow rather than shrink.

The check that takes five minutes

Before any Standard-to-Express migration, grep the state machine definitions for .sync, .waitForTaskToken, DISTRIBUTED map mode and Activity ARNs. Any hit ends the discussion — the workflow cannot move regardless of what the cost model says. Then list the downstream effects of every task state and mark each one idempotent or not. If that list has a single “not” on it, the answer is Standard, and you have saved yourself the fortnight this post opened with.

Closing Thought

Step Functions presents this as a workflow type, which makes it sound like a deployment detail. It is really a choice of execution guarantee, and the price follows from the guarantee rather than the other way round. A 200× cost difference between two things that look interchangeable is not a discount waiting to be claimed — it is the clearest possible signal that they are not interchangeable.

What makes it worth getting right the first time is that the type is fixed at creation. Most architectural mistakes on AWS are a migration away from being fixed. This one is a new ARN and every reference to it, which is enough friction that teams live with the wrong choice instead — writing compensating logic around a guarantee they did not want, indefinitely.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent