Task Decomposition
By the Syncfusion DevTutorials team · Last updated
How an agent breaks a big goal into small tasks, maps which tasks depend on which, plans sequential, parallel and priority order, and tracks progress until every task is proven done.
Think about cleaning a very messy room.
"Clean the room" can feel like a very large task. So you break it into smaller tasks: pick up the clothes, put the books on the shelf, take out the rubbish, and then vacuum the floor.
Some tasks must wait for others. For example, you cannot vacuum the floor until the floor is clear.
Other tasks do not depend on each other. A friend could put away the books while you pick up the clothes.
Breaking a large task into smaller, ordered tasks is called task decomposition. Agents use this approach all the time.
The main idea: A large goal is broken into smaller tasks. Some tasks depend on other tasks and must wait. Tasks that do not depend on each other can be done at the same time.
A Real-World Example
In this lesson, the Brightgrid project has two bugs:
web/format.jsprints "80000 rows" instead of "80,000 rows".api/limits.jssays the free plan can export 500 rows instead of 5,000.
The goal is still only one line: "Make every test pass." A good agent breaks that goal into four tasks:
- Run the tests to find out what is failing.
- Fix
web/format.js. - Fix
api/limits.js. - Verify that all tests pass.
Task 1 must happen first. Tasks 2 and 3 both depend on task 1, but they do not depend on each other. Task 4 depends on both fixes being completed.
In simple words: Task decomposition means turning "do the big thing" into "do these smaller tasks in the correct order".
What You Will Learn
In this lesson, you will learn:
- How to analyze a goal and break it into subtasks.
- How to identify which tasks depend on other tasks.
- How to organize tasks in sequential, parallel, and priority order.
- How to track progress, verify each task, and combine the results.
The Agent Lab includes a Plan tab that displays the agent's tasks as a board. After the first test run, the board is populated with tasks, and each task moves from waiting to in progress to done. Tasks in the same wave can run at the same time.
Two bugs: break the goal into tasks
Run it, then open the Plan tab. Step 2's thought lists the four tasks. The two fix tasks are placed in the same wave because both depend only on the first test run.
Goal Breakdown
Goal Analysis
Goal analysis means understanding what the goal requires before starting any work. For a coding agent, running the tests is often the fastest way to discover what is broken because failing tests show exactly where the problems are.
You cannot break down a goal effectively until you understand what it contains.
Subtask Creation
A subtask is a small part of a goal that can be completed and verified on its own. "Fix web/format.js" is a good subtask. "Fix stuff" is not.
A useful rule is to create one subtask for each separate problem.
The next example converts a list of failing tests into subtasks. It creates one fix task for each file, even when a file contains multiple failing tests.
Failing tests become subtasks
const failures = [
{ test: "formats a big number", file: "web/format.js" },
{ test: "formats a small number", file: "web/format.js" },
{ test: "free plan limit", file: "api/limits.js" },
];
const files = [...new Set(failures.map((f) => f.file))];
const subtasks = ["Run the tests", ...files.map((file) => "Fix " + file), "Check every test passes"];
subtasks.forEach((task, i) => console.log(i + 1 + ". " + task));
Expected output:
1. Run the tests
2. Fix web/format.js
3. Fix api/limits.js
4. Check every test passes
Three failing tests become only two fix tasks. Both failures in web/format.js can be solved with a single code change.
Dependency Mapping
A dependency means one task must be completed before another task can begin. A dependency map shows which tasks each task depends on.
The dependency map helps determine a safe execution order. Any task whose dependencies have already been completed is ready to start.
A dependency map gives a safe order
const needs = {
"Run the tests": [],
"Fix web/format.js": ["Run the tests"],
"Fix api/limits.js": ["Run the tests"],
"Check every test passes": ["Fix web/format.js", "Fix api/limits.js"],
};
const done = [];
while (done.length < Object.keys(needs).length) {
const ready = Object.keys(needs).filter((task) => !done.includes(task) && needs[task].every((n) => done.includes(n)));
console.log("Ready now: " + ready.join(", "));
done.push(...ready);
}
Expected output:
Ready now: Run the tests
Ready now: Fix web/format.js, Fix api/limits.js
Ready now: Check every test passes
Each line represents a wave. A wave is a group of tasks that are ready to start at the same time. This is the same concept used by the waves shown in the Plan tab.
Execution Planning
Sequential Plans
A sequential plan runs tasks one after another in a fixed order. It is simple and safe, but it is also the slowest approach.
Parallel Plans
A parallel plan runs tasks in the same wave at the same time. This makes the plan faster, but it only works when the tasks do not depend on each other.
The next example compares the time required for both approaches. Each task is assigned a duration in minutes.
Sequential vs parallel time
const waves = [
[{ task: "Run the tests", minutes: 2 }],
[{ task: "Fix web/format.js", minutes: 5 }, { task: "Fix api/limits.js", minutes: 4 }],
[{ task: "Check every test passes", minutes: 2 }],
];
const all = waves.flat();
const sequential = all.reduce((sum, t) => sum + t.minutes, 0);
const parallel = waves.reduce((sum, wave) => sum + Math.max(...wave.map((t) => t.minutes)), 0);
console.log("Sequential: " + sequential + " minutes");
console.log("Parallel: " + parallel + " minutes");
Expected output:
Sequential: 13 minutes
Parallel: 9 minutes
In a parallel plan, a wave takes as long as its slowest task. The two fixes take 5 minutes together instead of 9 minutes. That saves 4 minutes.
Priority Ordering
Priority ordering determines which ready task should be completed first when all tasks cannot be done at the same time. For example, fix a bug that customers can see before fixing one that customers cannot see.
Ready tasks sorted by priority
const ready = [
{ task: "Fix the typo on the help page", priority: 3 },
{ task: "Fix the wrong export limit", priority: 1 },
{ task: "Fix the row count text", priority: 2 },
];
const order = [...ready].sort((a, b) => a.priority - b.priority);
order.forEach((t, i) => console.log(i + 1 + ". " + t.task + " (priority " + t.priority + ")"));
Expected output:
1. Fix the wrong export limit (priority 1)
2. Fix the row count text (priority 2)
3. Fix the typo on the help page (priority 3)
Priority 1 goes first. A wrong export limit can prevent a customer from completing their work, so it has a higher priority than a typo.
The agent in the Agent Lab works on one task at a time, so it fixes api/limits.js first and web/format.js second. The board still shows both fixes in the same wave because they could be completed in parallel.
Easy Way to Remember: break it, map it, order it. Break the goal into tasks. Map the dependencies between tasks. Then order the ready tasks by wave and priority.
Progress Tracking
Task Status
Each task has a status: waiting, in progress, or done. Counting how many tasks are in each status shows the agent's progress.
Completion Verification
Completion verification means confirming that a task is actually finished before marking it as done. "I changed the file" is not proof. "The tests for that file now pass" is proof.
In the Plan tab, a fix task stays in progress after the file is changed. It only becomes done when a later test run shows that the tests for that file pass.
Result Aggregation
Result aggregation means combining the results of all completed tasks into a single final report.
Track, verify and combine the results
const tasks = [
{ task: "Run the tests", changed: false, testsPass: true },
{ task: "Fix web/format.js", changed: true, testsPass: true },
{ task: "Fix api/limits.js", changed: true, testsPass: false },
{ task: "Check every test passes", changed: false, testsPass: false },
];
const status = (t) => (t.testsPass ? "done" : t.changed ? "in progress" : "waiting");
for (const t of tasks) console.log(status(t) + " - " + t.task);
const doneCount = tasks.filter((t) => status(t) === "done").length;
console.log("Report: " + doneCount + " of " + tasks.length + " tasks done (" + Math.round((doneCount / tasks.length) * 100) + "%)");
Expected output:
done - Run the tests
done - Fix web/format.js
in progress - Fix api/limits.js
waiting - Check every test passes
Report: 2 of 4 tasks done (50%)
api/limits.js was changed, but its tests still fail. Because of that, its status remains in progress, not done. This simple rule prevents the agent from reporting success before the work is actually verified.
Never mark a task as done just because the agent performed an action. Mark it as done only after a check proves the task was completed successfully.
Common Mistakes
| Mistake | Incorrect Understanding | Correct Understanding | What Can Go Wrong |
|---|---|---|---|
| Planning without enough information | "I can create a plan from the goal alone." | Run the tests first, then create the plan. | The plan focuses on the wrong problem. |
| Tasks are too large | "Fix everything" is a single task. | Create one task for each problem. | It becomes difficult to track progress and completion. |
| No dependency map | "The order does not matter." | Document what each task depends on. | A task starts before the information or work it needs is ready. |
| Wrong tasks running in parallel | "Run everything at the same time." | Run only tasks in the same wave in parallel. | Tasks may conflict with the same files or data. |
| Marking tasks done too early | "I changed the file, so the task is done." | A task is done only when a verification check passes. | The final report shows success even though tests are still failing. |
Exercises
Each exercise ends with a value on the last line. Press Check to compare it with the expected answer.
What to Remember
- Understand what the goal requires before breaking it into smaller tasks.
- Create one task for each separate problem and record the dependencies between tasks.
- Tasks whose dependencies are complete are ready to start. A group of ready tasks is called a wave.
- In parallel execution, a wave takes as long as its slowest task.
- A task should be marked as done only when a verification check proves it is complete.
Next: Planning Strategies, three ways an agent can plan: ReAct, Plan-and-Execute and Reflexion.