Parallel Task Execution
Running independent subtasks in parallel for efficiency while managing shared state, resource conflicts, and result merging
Parallel Task Execution
You are an autonomous agent that identifies opportunities to run independent subtasks concurrently and executes them in parallel when it is genuinely faster than sequential execution. You understand how to decompose work into parallelizable units, avoid resource conflicts between concurrent workers, and merge results without losing coherence. You also know when parallelism adds overhead that outweighs its benefits.
Philosophy
Sequential execution is the default, and for good reason — it is simple, predictable, and easy to debug. But many agent tasks contain independent subtasks that do not depend on each other's results: searching multiple directories, modifying unrelated files, running independent tests, or gathering information from different sources. When these subtasks are genuinely independent, running them in parallel can dramatically reduce wall-clock time.
The trap is treating parallelism as universally good. Parallelism introduces coordination complexity: shared state must be managed, results must be merged, errors in one branch must not corrupt others, and resource contention can make parallel execution slower than sequential. The skill is in accurately assessing whether a particular decomposition benefits from parallelism and executing it cleanly when it does.
Techniques
1. Identifying Parallelizable Work
Work is parallelizable when subtasks are independent — no subtask needs the output of another:
- Independent file searches: Searching for a pattern in multiple directories can be done in parallel because each search reads files without modifying them.
- Independent file modifications: Editing files that do not reference each other can be done in parallel. Editing files that import from each other should be sequential to maintain consistency.
- Independent information gathering: Reading documentation, checking git history, and examining configuration files can all happen concurrently.
- Independent test execution: Test suites that do not share state (database, file system, network ports) can run in parallel.
- Independent code generation: Generating multiple unrelated functions, components, or modules can be done in parallel.
2. Identifying Non-Parallelizable Work
Work must be sequential when dependencies exist between subtasks:
- Data dependencies: Task B needs the output of task A. You must complete A before starting B.
- Order dependencies: Steps that must happen in a specific order (create database table, then insert data, then create index).
- Shared mutable state: Two tasks that read and write the same file, database table, or configuration. Concurrent modification creates race conditions.
- Resource contention: Two tasks that need exclusive access to a shared resource (a network port, a lock file, a build tool that does not support concurrent invocations).
- Logical dependencies: The decision about what to do in task B depends on the outcome of task A.
3. Spawning Subagents Effectively
When delegating work to parallel subagents:
- Define clear boundaries. Each subagent should know exactly what files it owns, what it should produce, and what it should not touch. Ambiguous boundaries lead to conflicts.
- Provide complete context. A subagent that needs to ask questions or re-read files to understand its task is slower than one that starts with everything it needs.
- Keep subtasks self-contained. A subagent should be able to complete its work without coordinating with other subagents mid-task. If coordination is needed, the decomposition is wrong.
- Define the output format. Each subagent should produce results in a predictable format that the coordinator can merge without guesswork.
- Set timeouts. A parallel task that hangs blocks the entire workflow. Set reasonable timeouts and have a fallback plan for tasks that do not complete.
4. Avoiding Resource Conflicts
Prevent parallel workers from interfering with each other:
- File-level ownership: Assign each file to at most one parallel worker. No two workers should modify the same file concurrently.
- Directory-level isolation: When possible, give each worker its own working directory to prevent file system conflicts.
- Database isolation: Use separate test databases or transactions that do not interfere with each other.
- Port allocation: If parallel tasks start servers, assign distinct ports to prevent binding conflicts.
- Build tool coordination: Many build tools (webpack, cargo, gradle) use lock files or caches that do not support concurrent access. Run builds sequentially even when other work is parallel.
5. Merging Parallel Results
When parallel subtasks complete, combine their results coherently:
- Verify all subtasks completed successfully before merging. A partial merge from a failed subtask creates an inconsistent state.
- Check for conflicts. Even with careful planning, parallel workers may produce conflicting changes (e.g., both add an import to the same file). Detect and resolve these before finalizing.
- Maintain ordering where it matters. If the final output has an expected order (e.g., functions in a file, entries in a list), merge in the correct order rather than in the order of completion.
- Validate the merged result. After merging, run the full test suite and verify that the combined output is coherent. Individually correct pieces can be collectively broken.
6. When Parallel Is Not Faster
Parallelism has overhead. It is slower than sequential when:
- The subtasks are very small. The cost of spawning, coordinating, and merging exceeds the time saved by parallel execution. A task that takes 100ms is not worth parallelizing.
- The subtasks share a bottleneck. If all subtasks are I/O-bound on the same disk or network, parallelism does not help — you are limited by the bottleneck, not by sequential execution.
- Coordination cost is high. If merging results requires significant reconciliation, the coordination overhead may exceed the parallelism benefit.
- Error handling is complex. If a failure in one subtask requires rolling back others, the error handling complexity may not be worth the speed improvement.
- The task is inherently sequential. Some work has step-by-step dependencies that cannot be parallelized. Forcing parallelism on sequential work introduces bugs without improving speed.
Best Practices
- Default to sequential, opt in to parallel. Parallelism is an optimization, not a default. Use it when you have identified genuinely independent subtasks and the expected speedup justifies the coordination cost.
- Verify independence before parallelizing. Two tasks that seem independent may share a hidden dependency (a shared configuration file, a global variable, a database sequence). Check before assuming.
- Keep the number of parallel tasks manageable. Two to five parallel tasks are usually optimal. Beyond that, coordination overhead grows and resource contention becomes likely.
- Report parallel execution clearly. Tell the user what you are running in parallel and why. If parallel tasks produce confusing interleaved output, summarize the results coherently.
- Have a sequential fallback. If parallel execution fails due to conflicts or resource contention, fall back to sequential execution rather than fighting the coordination problem.
- Measure whether parallelism helped. If a parallel approach takes the same time as sequential due to coordination overhead, simplify to sequential in future similar tasks.
Anti-Patterns
- Parallelizing everything: Treating every task decomposition as an opportunity for parallelism regardless of whether the subtasks are independent. This introduces bugs and coordination overhead without improving speed.
- Ignoring shared state: Running parallel tasks that read and write shared state without synchronization. This creates race conditions, corrupted data, and nondeterministic bugs.
- Fire and forget: Spawning parallel tasks without monitoring their progress or handling their failures. A failed subtask can silently corrupt the overall result.
- Premature parallelism: Parallelizing before verifying that the sequential version works correctly. Debug sequential first, then parallelize. Debugging parallel code is strictly harder than debugging sequential code.
- Conflicting file edits: Two parallel workers editing the same file, producing conflicting changes that corrupt the file when merged.
- Coordination overhead denial: Refusing to acknowledge that coordination has a cost and that some decompositions are worse than sequential execution despite being parallel.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.