Every smart contract is a sequence of operations. But how you sequence them—one after another, in parallel batches, or asynchronously—determines your contract's security, gas cost, and composability. This guide compares sequential and parallel workflow patterns for smart contract developers who need to choose an execution model before writing a single line of Solidity.
Who Must Choose and Why the Decision Matters Now
If you're building a contract that handles multiple token transfers, aggregates oracle data, or coordinates cross-chain messages, you've already faced the question: should these steps run in strict order, or can some execute simultaneously? The answer isn't academic—it affects reentrancy exposure, gas limits, and the user experience when a transaction fails midway.
Consider a typical DeFi interaction: a user deposits collateral, borrows an asset, and swaps part of it. In a sequential workflow, each step depends on the previous one's state. If the swap fails, the entire transaction reverts, leaving the user's position unchanged. That atomicity is often desirable, but it also means a single failure blocks the whole operation. In contrast, a parallel workflow might allow the deposit and the swap to proceed independently, then reconcile state afterward. This can improve throughput but introduces partial-failure scenarios that must be handled explicitly.
The decision point arrives early, during the architecture phase. Teams that defer this choice often end up retrofitting concurrency controls into a sequential design—a painful refactor that can introduce subtle bugs. We've seen projects where a seemingly simple sequential loop over token transfers turned into a gas bomb once the number of recipients exceeded block limits. The workflow model constrains what your contract can do, so choosing consciously is essential.
This guide is for smart contract developers, auditors, and technical leads evaluating execution patterns. We'll compare three approaches—serial steps, batched parallel, and asynchronous pipelines—using criteria that matter in production: gas efficiency, atomicity, state complexity, and failure handling. By the end, you'll have a framework for picking the right workflow for your next contract.
The Workflow Landscape: Three Patterns for Smart Contracts
Smart contract workflows fall into three broad categories, each with distinct characteristics. Understanding these patterns is the first step toward making an informed choice.
Sequential (Serial Steps)
This is the default in Ethereum Virtual Machine (EVM) languages like Solidity. Functions execute line by line, and a revert at any point undoes all prior state changes. Sequential workflows are easy to reason about, audit, and test. They guarantee atomicity—either everything succeeds or nothing changes. However, they are inherently linear: total gas cost is the sum of each step, and the transaction must fit within the block gas limit. For workflows with many steps, this can be a hard cap.
Batched Parallel (Multi-call Patterns)
Patterns like the multicall contract or delegatecall batches allow multiple independent operations to be grouped into a single transaction. Each sub-call can succeed or fail independently, and the contract can choose to continue processing even if some calls revert. This pattern is common in aggregators, batch token transfers, and DAO proposal executions. The key trade-off: you gain throughput and flexibility but lose the atomicity guarantee of a sequential flow. Partial failures must be handled explicitly—often by tracking success flags and reconciling state after the batch completes.
Asynchronous Pipelines (Event-Driven)
In this pattern, a contract emits an event or sends a message to trigger off-chain or cross-chain execution. The next step is not guaranteed to happen in the same transaction. This is typical for layer-2 rollups, cross-chain bridges, and oracle updates. Asynchronous workflows decouple steps in time, which can reduce per-transaction gas costs and allow complex multi-step processes. However, they introduce latency, require off-chain infrastructure, and complicate state management. The contract must be able to handle incomplete states and timeouts.
Each pattern has a place. Sequential is safest for value-critical operations where partial success is unacceptable. Batched parallel works well for independent operations that can be aggregated. Asynchronous pipelines are necessary when steps span multiple blocks or chains.
Criteria for Choosing a Workflow Pattern
To compare these patterns objectively, we need a consistent set of criteria. The following factors should drive your decision.
Gas Cost Predictability
Sequential workflows have predictable gas costs—each step adds its gas to the total. But if the number of steps varies (e.g., iterating over a dynamic array), the cost can exceed block limits. Batched parallel can reduce gas by avoiding redundant overhead (e.g., multiple SLOAD operations for the same storage slot), but the cost depends on how many sub-calls succeed or fail. Asynchronous pipelines shift gas costs across transactions, which can help stay within limits but makes total cost harder to estimate upfront.
Atomicity Requirements
If every operation in your workflow must succeed or the entire transaction must revert, sequential is the natural fit. Batched parallel can simulate atomicity by checking success flags and reverting if any call failed, but this adds complexity. Asynchronous pipelines cannot guarantee atomicity across steps—once the first transaction is mined, later steps may fail independently.
State Complexity and Reentrancy
Sequential workflows simplify state reasoning because state changes are linear and reversible. They are also easier to protect against reentrancy—a simple mutex or checks-effects-interactions pattern works well. Batched parallel increases state complexity because multiple sub-calls may modify overlapping storage. If sub-calls interact with the same contract, reentrancy risks multiply. Asynchronous pipelines require the contract to handle incomplete states, which often means storing intermediate results and checking invariants at each step.
Composability with External Protocols
Sequential workflows integrate naturally with external contracts via direct calls. Batched parallel can compose with multiple external contracts in one transaction, but each external call must be trusted or checked for expected behavior. Asynchronous pipelines require external protocols to support event-driven or callback-based interactions, which not all protocols do.
Use these criteria to map your specific requirements to a pattern. No single pattern wins on all fronts; the best choice depends on which constraints matter most for your use case.
Trade-offs at a Glance: A Structured Comparison
The table below summarizes how each pattern performs across the key criteria. Use it as a quick reference when evaluating your workflow.
| Criterion | Sequential | Batched Parallel | Asynchronous Pipeline |
|---|---|---|---|
| Gas predictability | High (sum of steps) | Medium (depends on sub-call results) | Low (spread across txs) |
| Atomicity | Full (all-or-nothing) | Partial (can simulate full) | None across steps |
| State complexity | Low | Medium | High |
| Reentrancy risk | Low (standard patterns work) | Medium (cross-call interactions) | High (state must be validated at each step) |
| Composability | High (direct calls) | High (multi-call) | Medium (requires event/callback support) |
| Block gas limit risk | High for many steps | Medium (can batch independent ops) | Low (per-step gas is small) |
To ground these trade-offs, consider two scenarios:
Scenario A: Token Swap with Slippage Protection. A user wants to swap ETH for USDC on a DEX, then stake the USDC in a yield farm. Sequential is ideal: if the swap fails, the stake should not happen. The atomicity protects the user from partial loss. Batched parallel would be risky because the stake could execute even if the swap returned fewer tokens than expected, unless the contract explicitly checks the swap output before staking.
Scenario B: Batch NFT Minting. A collection allows users to mint multiple NFTs in one transaction. Each mint is independent—failure of one should not block others. Batched parallel is a natural fit: the contract loops over mint requests, tracks successes, and emits events for each. Sequential would force all mints to succeed or revert, which could frustrate users if one mint fails due to a temporary issue (e.g., metadata fetch). Asynchronous would be overkill here, adding unnecessary complexity.
These examples show how the same pattern can be right for one use case and wrong for another. The trade-off table helps you spot the fit quickly.
Implementing Your Chosen Workflow: Practical Steps
Once you've selected a pattern, the implementation details matter. Below are concrete steps for each approach, with Solidity examples and testing strategies.
For Sequential Workflows
Write functions that follow the checks-effects-interactions pattern. Use modifiers or require statements to validate inputs before state changes. Test with a framework like Foundry or Hardhat, using fuzzing to ensure that reverts at any step leave state unchanged. A common pitfall is forgetting to update a mapping before an external call—this opens reentrancy. Always update state first, then call external contracts.
For Batched Parallel Workflows
Design a struct to hold the success status and return data for each sub-call. Use a loop that executes each call via low-level call or delegatecall. After the loop, iterate again to check results and revert if a critical call failed, or continue if failures are acceptable. Gas estimation is tricky because the cost depends on the number of successful calls. Use a gas limit per sub-call to prevent one failing call from consuming all gas. Test with varying numbers of sub-calls and failure scenarios.
For Asynchronous Pipelines
Define a state machine with explicit phases (e.g., PENDING, COMPLETED, FAILED). Store intermediate results in mappings or structs. Use events to signal off-chain workers to execute the next step. Implement timeout mechanisms to handle cases where a step never arrives. Testing requires simulating multi-block scenarios—use hardhat_setNextBlockBaseFee or similar tools to manipulate block context. Security is paramount: validate that each step can only be called by the authorized executor and that state transitions are correct.
Regardless of pattern, invest in comprehensive unit tests and integration tests that cover edge cases: empty inputs, maximum batch sizes, reentrancy attempts, and gas exhaustion. Consider formal verification for critical invariants, especially in parallel workflows where state interactions are complex.
Risks of Choosing the Wrong Workflow
Selecting an inappropriate workflow pattern can lead to severe consequences. Here are the most common failure modes.
Deadlocks and Livelocks
In batched parallel workflows, if two sub-calls attempt to acquire the same lock (e.g., a mutex on a shared pool), the transaction can deadlock. This is rare in EVM because contracts don't have native locks, but it can happen if sub-calls call back into the same contract. For example, a multicall that both deposits and withdraws from the same pool could cause a reentrancy loop. The fix is to carefully order sub-calls and avoid circular dependencies.
Partial State Updates and Inconsistencies
In batched parallel workflows, if a sub-call fails and the contract continues, the state may be left in an inconsistent state. For instance, a batch transfer that succeeds for some recipients but fails for others could leave the sender's balance unchanged while some recipients have already received tokens. The contract must either revert the entire batch (simulating atomicity) or implement a reconciliation mechanism. Many teams underestimate the complexity of handling partial failures correctly.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!