Spring Batch Job Flow — Conditional Transitions, Deciders, and Splits
Fast recall
Quick revision
Core answer
JobBuilder DSL supports three flow primitives: conditional transitions (start(a).on("FAILED").to(b).from(a).on("*").to(c)) for routing on a step's exit status, `JobExecutionDecider` for branching on arbitrary runtime state (job parameters, ExecutionContext, business rules) without coupling logic to a step's exit status, and `split(taskExecutor).add(flowA, flowB)` for executing independent flows in parallel and joining their results. Use transitions for simple success/failure routing, deciders when the branch depends on data computed at runtime rather than a step outcome, and splits when independent steps can safely run concurrently.Whether you can compose non-trivial job flows beyond a linear sequence of steps, and pick the right primitive for the routing requirement
Setting a step's exit status from inside a Tasklet to drive routing, when a `JobExecutionDecider` would express the intent more cleanly
Discuss the `FlowJobBuilder` underneath, how `on("*")` matches everything including FAILED, and how `split` joins partition restart state across the parallel flows
Interview-ready guide
Interview answer
Spring Batch gives you three flow primitives that cover almost every real batch topology: conditional transitions, JobExecutionDecider, and split.
Conditional transitions:
start(a).on("FAILED").to(alertStep).from(a).on("*").to(nextStep)- Routes on a step's
ExitStatus, which by default mirrorsBatchStatus - A
StepExecutionListenercan override exit status to capture nuance likeCOMPLETED_WITH_SKIPS - Watch for
on("*")— it matches FAILED too; useon("COMPLETED")for success-only routes
`JobExecutionDecider`:
- For branching on runtime state beyond exit status — job parameters, step counts, business rules
- Returns a
FlowExecutionStatusthat the DSL routes on - Must be deterministic — given the same
JobExecution/StepExecutionit must return the same status, or restart breaks - Pure routing logic only; put work in steps, not deciders
`split(taskExecutor).add(flowA, flowB)`:
- Runs independent flows in parallel using a
TaskExecutor - Joins all branches before the next step runs
- Each flow has its own
StepExecutionrecords and restarts independently - Pitfalls: hidden data dependencies between flows, single-thread executor that defeats the purpose, long-tail flow blocking the join
Decision rule:
- Linear chain when no branching is needed
- Conditional transitions for simple success/failure routing
- Decider when the branch depends on runtime data, not just exit status
- Split when independent flows can safely run concurrently and wall-clock matters
In an interview, I'd emphasize that overloading exit status with business meaning is the most common smell — that's what JobExecutionDecider exists for.
Spring Batch Job Flow — Conditional Transitions, Deciders, and Splits
Direct Answer
Spring Batch's JobBuilder DSL supports three flow primitives: conditional transitions (start(a).on("FAILED").to(b).from(a).on("*").to(c)) for routing on a step's exit status, JobExecutionDecider for branching on arbitrary runtime state (job parameters, ExecutionContext, business rules) without coupling logic to a step's exit status, and split(taskExecutor).add(flowA, flowB) for executing independent flows in parallel and joining their results. Use transitions for simple success/failure routing, deciders when the branch depends on data computed at runtime rather than a step outcome, and splits when independent steps can safely run concurrently.
Common Mistakes & Pitfalls
Setting a step's exit status from inside a Tasklet to drive routing, when a JobExecutionDecider would express the intent more cleanly
Check your understanding
Answer these without looking back at the article.
- Why does `on("*")` match FAILED steps, and how do you write a route that runs only on success?
- How does a JobExecutionDecider behave when a job is restarted from a step that runs after the decider?
- What happens to in-flight flows when one branch of a split fails?
- When would you use a custom ExitStatus from a StepExecutionListener vs a JobExecutionDecider?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.