skip to content

In AWS Step Functions, what are the required top-level fields of an Amazon States Language definition, and how do StartAt, Next and End control the flow?

level: juniorimportance: should knowfreq 52%

answer

  1. two mandatory top-level fields
  2. the entry point is named, not positional
  3. successors are explicit, no fall-through
  4. branch targets live inside Choices
  5. End: true terminates that path

basics

~20 s

An Amazon States Language definition requires two top-level fields: StartAt and States. StartAt names the first state to run, each state's Next names the state that follows it, and End set to true finishes that branch.

solid answer

~50 s

An ASL definition is a JSON document whose only mandatory top-level fields are `States` — a map of state name to state definition — and `StartAt`, which must match one of those names. Everything else (`Comment`, `TimeoutSeconds`, `Version`, `QueryLanguage`) is optional. Every state carries a `Type`: `Task` does the work, `Choice` branches on the state's input, `Wait` pauses, `Pass` injects or reshapes data, `Parallel` and `Map` fan out, and `Succeed`/`Fail` stop the execution. Control flow is fully explicit — a state names its successor with `Next` or sets `End: true` — so the order the states appear in the JSON is irrelevant; there is no fall-through to the next block. `Choice` is the exception: it has no `Next` of its own, each rule inside `Choices` carries one, plus an optional `Default`. The terminal states `Succeed` and `Fail` take neither.

go deeper

for a junior

Be able to write a two-state definition from memory: StartAt, a States map, a Task with Next, and a terminal state. Say plainly that transitions are explicit, never positional.

for a middle

Explain what each state Type is for and why Choice puts the transition on each rule instead of on the state. Be ready to describe how a state's output becomes the next state's input.

for a senior

Show judgment about definition size and shape: when Choice sprawl means the logic belongs in code, how payload trimming keeps a workflow maintainable, and how an explicit graph makes an incident reviewable.

for a principal

Own the argument for a declarative definition at all — an auditable, diffable control flow that the platform persists, versus orchestration hidden in application code that your team must then make durable itself.

## What the document is A Step Functions state machine is defined by an Amazon States Language (ASL) document — JSON (the console and CLI also accept YAML in some tooling, but the service stores JSON). The document is a *graph*, not a script: it declares named states and the edges between them, and the service walks that graph, persisting the position and payload after every step. That durability is the whole point of the product, and it is why the language is deliberately small. ## The two required fields Only two top-level fields are mandatory: - `States` — an object mapping each state's **name** (the key) to its definition (the value). Names are arbitrary strings and are what you see in the console graph and in the execution history. - `StartAt` — a string that must exactly match one of the keys in `States`. It is the entry point. Everything else is optional metadata or defaults: `Comment` (free text), `TimeoutSeconds` (a ceiling for the whole execution), `Version` (the ASL spec version), and — on newer state machines — `QueryLanguage`, which selects JSONPath (the long-standing default) or JSONata for expressions. ```json { "Comment": "Minimal two-step workflow", "StartAt": "Validate", "States": { "Validate": { "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "Parameters": { "FunctionName": "validate-order" }, "Next": "Done" }, "Done": { "Type": "Succeed" } } } ``` ## Every state has a Type `Type` is required on every state and decides what the state can contain: - **Task** — the only state that calls something outside the workflow. It carries a `Resource` ARN identifying the integration (a Lambda function, an optimized service integration, an SDK integration, or an Activity). - **Choice** — branches. It holds a `Choices` array of rules, each rule a comparison plus a `Next`, and optionally a `Default` for when no rule matches. If nothing matches and there is no `Default`, the execution fails. - **Wait** — pauses for `Seconds` or until a `Timestamp` (or the `SecondsPath`/`TimestampPath` variants that read the value out of the payload). The wait is durable: the service is not holding a thread for you. - **Pass** — passes its input through, optionally replacing it with a literal `Result` or reshaping it. Useful as a stub while you build, and as a place to normalize a payload. - **Parallel** — runs a fixed set of `Branches` concurrently, each branch its own mini state machine; the output is an array of branch outputs, in branch order. - **Map** — runs the *same* branch once per element of an input array. - **Succeed** / **Fail** — terminal. `Fail` also carries an `Error` and `Cause` that surface in the execution history. ## How flow is expressed The single most common beginner mistake is assuming states run in the order they are written. They do not. `States` is a JSON object — a map — and maps have no meaningful order. The graph is built purely from `StartAt` and the `Next` pointers, so reformatting or alphabetizing the definition changes nothing about execution. Every non-terminal state must supply exactly one of `Next` or `End: true`; omitting both is a definition error and `CreateStateMachine` rejects the document rather than failing at run time. `End: true` means "this path finished successfully", and the state's output becomes the output of the execution (or of the enclosing branch/iteration, when you are inside `Parallel` or `Map`). Two shapes deviate: - `Choice` never has its own `Next`; the transition lives on each rule. - `Succeed` and `Fail` are terminal by definition, so they accept neither `Next` nor `End`. ```json "IsPremium": { "Type": "Choice", "Choices": [ { "Variable": "$.tier", "StringEquals": "premium", "Next": "FastPath" } ], "Default": "StandardPath" } ``` ## Data between states Each state receives a JSON payload as input and emits one as output; the output of a state is the input of whatever `Next` names. The path fields (`InputPath`, `Parameters`, `ResultSelector`, `ResultPath`, `OutputPath`) let you select, build, merge and trim that payload without writing code. A workflow that threads whole objects through every state grows the payload for no reason — trimming with `ResultSelector` or `OutputPath` at each step is the normal hygiene. ## Why the explicitness matters Because every edge is named, the console can render the graph, the execution history can point at the exact state that ran, and a reviewer can diff a definition and see a control-flow change. That property is what you are buying over a hand-rolled chain of function calls — and it is why the language refuses conveniences like implicit fall-through.

  • What happens if a Task state has neither Next nor End?
    The definition is invalid, so it is rejected when you create or update the state machine rather than failing mid-execution. Every non-terminal state must declare exactly one transition — either a `Next` naming another state, or `End: true`. Catching this at definition time is deliberate: control-flow gaps never reach production traffic.
  • Does the order of states inside the States object change how the workflow runs?
    No. `States` is a JSON map, and the graph is built only from `StartAt` plus the `Next` pointers. Reordering or alphabetizing the definition produces an identical execution. The visual order in the console likewise comes from the edges, not from the file layout.
  • When is a Pass state actually worth adding?
    Two cases: as a placeholder while the real Task is still being written, so the graph is complete and runnable end to end; and as a normalizer, where `Parameters` reshapes an awkward payload into the structure the next state expects. It costs a state transition, so do not sprinkle them for cosmetics.

saying these in an interview costs you the question

  • Assuming states execute top to bottom as written
  • Thinking a Choice state needs its own Next field
  • Giving a Succeed or Fail state a Next transition
  • Believing StartAt is optional if there is one state
  • Treating ASL as a scripting language with fall-through

context