Task Decomposition

Large tasks balloon context and hurt accuracy. Break them into focused units where each agent call has minimal, relevant context.

Intermediate 11 min read

Why Decompose Tasks

When you give an AI agent a large task ("add user authentication to the app"), two problems emerge:

  1. Context bloat - the agent reads every file it thinks might be relevant, accumulating 50k+ tokens of context
  2. Accuracy drop - with a huge context, the model may lose track of constraints stated earlier, produce inconsistent code, or miss edge cases

Decomposing into focused subtasks keeps each call's context minimal and gives the model a clear, single goal.

Monolith vs Unit Tasks

Monolith task (expensive, risky)
# ONE big task:
"Add a complete user authentication system including:
 - Login and registration forms
 - Password hashing and verification
 - JWT or session tokens
 - Remember me functionality
 - Password reset via email
 - Role-based access control
 - Admin panel for user management"

# What happens:
- Agent reads 20+ files to understand the codebase
- Context grows to 80,000+ tokens
- Agent tries to hold all requirements in one pass
- Inconsistencies appear between frontend forms and backend logic
- Token cost: very high. Quality: often poor.
Unit tasks (efficient, accurate)
# SEVEN focused tasks:
Task 1: Create users table migration and UserModel with password hashing
        Context needed: /config/database.php, existing model template
        Tokens: ~3,000

Task 2: Build login form (POST /auth/login) using AuthController
        Context needed: Task 1 output (UserModel), routes.php
        Tokens: ~2,500

Task 3: Build registration form with validation
        Context needed: UserModel, existing form examples
        Tokens: ~2,000

Task 4: Implement session-based auth with remember_me cookie
        Context needed: AuthController from Task 2, config/app.php
        Tokens: ~2,500

Task 5: Password reset flow (request email + reset form)
        Context needed: UserModel, Mailer class, email templates
        Tokens: ~3,000

Task 6: RBAC - add role column, middleware, @role annotations
        Context needed: UserModel, existing middleware pattern
        Tokens: ~2,500

Task 7: Admin user management panel
        Context needed: UserModel, admin layout template
        Tokens: ~3,000

Total tokens: ~18,500 (vs 80,000+ monolith)
Each task is accurate because it has focused, relevant context only

Decomposition Patterns

Three common ways to split coding tasks:

PatternSplit byBest for
Layer splitDB model, controller, view, testsCRUD features, form flows
File splitOne subtask per file or moduleRefactors, style fixes
Step splitResearch, plan, implement, verifyUnknown codebases, complex bugs
Concern splitFrontend, backend, API, testsFull-stack features

Parallel Subagents

Tasks that do not depend on each other can run in parallel. Claude Code's Agent tool supports multiple concurrent subagents, and the Anthropic API can be called in parallel from your own code.

Python - Parallel agent calls
import anthropic
import concurrent.futures

client = anthropic.Anthropic()

def run_subtask(task: dict) -> dict:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=2048,
        system=task["system"],
        messages=[{"role": "user", "content": task["prompt"]}],
    )
    return {"task": task["name"], "result": response.content[0].text}

# Independent tasks that can run simultaneously
subtasks = [
    {
        "name": "Write UserModel",
        "system": "PHP 8.2 developer. PDO only. No raw SQL.",
        "prompt": f"Write UserModel class with findByEmail(), create(), hashPassword(). Schema: {db_schema}",
    },
    {
        "name": "Write UserModelTest",
        "system": "PHPUnit developer. Mock the DB layer.",
        "prompt": "Write PHPUnit tests for UserModel. Test findByEmail with valid/invalid email.",
    },
    {
        "name": "Write migration",
        "system": "MySQL 8 DBA.",
        "prompt": f"Write CREATE TABLE migration for users: {user_fields}",
    },
]

# Run all three in parallel
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    results = list(executor.map(run_subtask, subtasks))

for r in results:
    print(f"[{r['task']}]\n{r['result']}\n")

Sequential Chaining

When one task's output is required as input for the next, chain them sequentially and pass only the relevant output forward (not the full prior context):

Python - Sequential chain, minimal handoff
def chain_tasks(steps: list) -> list:
    results = []
    previous_output = ""

    for step in steps:
        # Only pass the RELEVANT part of the prior output, not everything
        prompt = step["prompt_template"].format(prior=previous_output[:2000])

        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            system=step["system"],
            messages=[{"role": "user", "content": prompt}],
        )
        previous_output = response.content[0].text
        results.append({"step": step["name"], "output": previous_output})

    return results

steps = [
    {
        "name": "Find bug location",
        "system": "PHP developer. Be concise.",
        "prompt_template": f"In auth.php lines 45-90, find the root cause of: session_regenerate_id fails. Report: file:line only. Code: {code_snippet}",
    },
    {
        "name": "Write fix",
        "system": "PHP developer. Show diff only.",
        "prompt_template": "Fix the issue found at {prior}. Show the corrected lines as a diff.",
    },
    {
        "name": "Write test",
        "system": "PHPUnit developer.",
        "prompt_template": "Write one test to verify the fix: {prior} is correct.",
    },
]
Truncate handoffs

When chaining tasks, resist the urge to pass the full output of step N to step N+1. Extract only the essential part: the file:line reference, the new function signature, the error type. Truncating handoffs is where sequential chains save the most tokens.

Practical Examples

TaskMonolith tokensDecomposed tokensSaving
Add CRUD feature~45,000~12,000 (4 subtasks)73%
Refactor large class~30,000~8,000 (method by method)73%
Debug and fix a test suite~25,000~7,000 (locate, fix, verify)72%
Write full test coverage~40,000~10,000 (1 class per call)75%

Frequently Asked Questions

A good subtask should be completable in one or two tool calls, produce a single clear output, and fit within 5-10k tokens of context. If a task requires reading more than 3-4 files or has multiple distinct outputs (frontend + backend + tests), split it further.

Not always - there is overhead for each orchestration call. Decomposition reduces total tokens when: (1) subtasks use very different context (avoiding irrelevant file reads), (2) some subtasks can be skipped if earlier ones reveal the problem, (3) parallel execution is possible. For simple 2-step tasks, one focused call is often better.

Claude Code supports spawning subagents via the Agent tool from within a running session. The orchestrator (main agent) can delegate research, implementation, and verification to separate subagent instances, each with their own focused context. This is the most powerful pattern for large tasks.

Each orchestration message (the instruction to the subagent and its response back) costs additional tokens. Typically 100-500 tokens per handoff. For tasks with 10+ subtasks this overhead is negligible compared to the savings from focused context. For 2-3 step tasks, evaluate if decomposition is worth it.