Serverless functions were originally sold as ideal for simple, stateless operations - a request comes in, does one thing, returns a response, forgets everything. Real business processes are rarely that simple; a lot of genuinely useful workflows span multiple steps, need to wait on external events, and require state to persist across those steps. Building this on raw, stateless serverless functions without the right pattern is where teams end up with fragile, hard-to-debug workflows held together with database polling and hope.
Why stateless functions and multi-step workflows are naturally in tension
A single serverless function execution is short-lived by design and doesn’t naturally remember anything about a previous step once it completes. A workflow like “process an order, wait for payment confirmation (which might take minutes or hours), then trigger fulfillment, then send a notification” spans multiple discrete events over an unpredictable timeframe - the naive approach of chaining function calls directly, hoping each step reliably triggers the next, breaks down quickly under real-world failure conditions: a function that fails mid-chain, a step that needs to wait longer than a function’s execution timeout allows, or a need to know exactly where a given order currently stands in the process.
The pattern that actually solves this: orchestration, not chaining
Workflow orchestration tools (AWS Step Functions, Temporal, or similar) exist specifically to manage this: they track the state of a multi-step process explicitly, handle retries and failures at each step with defined policies rather than ad-hoc error handling scattered across function code, and can wait for external events (a webhook, a timeout, a human approval) without needing to keep a function running the whole time incurring cost and hitting execution limits.
What this actually gives you over hand-rolled chaining
- Explicit, visible workflow state - at any point, you can see exactly where a given process instance currently is, rather than inferring it from scattered database records and log entries across multiple function invocations.
- Defined retry and error-handling policies per step, rather than each function implementing its own inconsistent error handling - a failed step retries according to a clear, centrally-defined policy, and a step that’s genuinely failed (not just transiently) routes to explicit failure handling rather than silently stalling the whole process.
- Long-running waits without holding resources - waiting for a payment confirmation that might take hours doesn’t require a function to stay running (and billing) that whole time; the orchestration layer handles the wait state efficiently.
Where we push back on adopting orchestration tooling
For genuinely simple, short workflows - two or three steps, minimal branching, no long external waits - a dedicated orchestration tool is real added complexity and infrastructure for a problem simple retry logic within a single function can handle just fine. We reach for orchestration specifically when a workflow has real complexity: multiple steps, meaningful wait times for external events, or genuine business value in being able to see and reason about exactly where a process instance stands.
What we actually build
Simple, short serverless functions stay simple, chained directly where that’s genuinely sufficient. Complex, multi-step business processes - order fulfillment, multi-stage approval workflows, anything with meaningful external waits - get built on proper orchestration tooling from the start, because retrofitting orchestration onto an already-fragile chain of ad-hoc function calls is a much harder, riskier migration than building it correctly the first time.
We architect this as part of our serverless and cloud infrastructure work. If your team is managing a complex business process through a chain of loosely-connected functions and it’s becoming fragile, talk to us about whether proper orchestration would fix the reliability problems you’re seeing.