How to Use Task Dependencies in Claude Code

Build sophisticated workflows where tasks know what must complete before they can start. Master addBlockedBy, addBlocks, and automatic unblocking for complex multi-phase projects.

Introduction

Complex projects rarely consist of independent tasks. More often, work flows in sequences where one task cannot begin until another completes. The CSS cannot be written until the HTML structure exists. The JavaScript interactions cannot be implemented until the DOM elements are in place. The backend API cannot be tested until the frontend form is ready to send data.

Without explicit dependency management, you track these relationships mentally. You remember that Task B needs Task A, that Task C needs both A and B, and that Task D is independent. As projects grow, this mental bookkeeping becomes unsustainable. You forget dependencies, start work prematurely, or waste time checking if prerequisites are met.

Claude Code's Task system solves this with built-in dependency tracking. Tasks can declare what they're blocked by and what they block. When a blocking task completes, dependents automatically become unblocked. You define the relationships once; the system enforces them throughout the project.

This article shows you how to use addBlockedBy and addBlocks to build sophisticated task workflows. You'll learn the patterns that work for sequential pipelines, parallel work that merges, and approval gates. By the end, you'll manage complex multi-phase projects with confidence.

Understanding Blockers

Every task in Claude Code can have two types of relationships with other tasks: things that block it, and things it blocks. Understanding this distinction is essential for building correct dependency graphs.

blockedBy: Tasks That Must Complete First

When a task is "blocked by" another task, it cannot proceed until that blocking task completes. The blocked task exists, it's defined, but it's not ready for work. Think of it as being in a waiting room.

A task that's blocked will show a blocked status in TaskList. Claude knows not to work on blocked tasks. The dependency is explicit and enforced, not just documented.

Conceptual Model
Task: "Add CSS styling"
  blockedBy: ["Create HTML structure"]
  status: blocked

Meaning: Cannot start CSS until HTML structure is complete.
The task exists but is not ready for work.

blocks: Tasks Waiting on This One

The inverse relationship is "blocks." When a task blocks another, completing this task will unblock the dependent. This is the same relationship viewed from the other direction.

You can define dependencies from either side. Saying "Task A blocks Task B" is equivalent to saying "Task B is blocked by Task A." Use whichever direction feels more natural when creating or updating tasks.

Conceptual Model
Task: "Create HTML structure"
  blocks: ["Add CSS styling", "Add JavaScript interactions"]
  status: in-progress

Meaning: When this task completes, CSS and JavaScript tasks
will automatically become unblocked and ready for work.

Checking If a Task Is Blocked

TaskList shows the blocked status of every task. Tasks that are blocked display their blocking dependencies, so you know exactly what's holding them up. This visibility is crucial for project planning and identifying bottlenecks.

TaskList Output
TaskList

ID    Title                      Status       Blocked By
------------------------------------------------------------
t1    Create HTML structure      in-progress  -
t2    Add CSS styling            blocked      t1
t3    Add JavaScript             blocked      t2
t4    Write unit tests           pending      -

Ready for work: t1, t4
Blocked: t2 (waiting on t1), t3 (waiting on t2)
Blocked vs Pending: A blocked task has explicit dependencies that must complete first. A pending task has no dependencies but hasn't been started yet. Both appear in TaskList, but only blocked tasks have the blockedBy field populated.

Setting Up Dependencies

You define dependencies when creating tasks with TaskCreate or when updating existing tasks with TaskUpdate. Both support addBlockedBy and addBlocks parameters that specify task IDs.

Using addBlockedBy in TaskUpdate

When you have a task that depends on another, use addBlockedBy to declare what must complete first. This is the most common approach when you're creating dependent tasks after their prerequisites exist.

TaskUpdate with addBlockedBy
TaskUpdate
  id: "css-styling"
  addBlockedBy: ["html-structure"]

Result: The css-styling task is now blocked by html-structure.
It will automatically unblock when html-structure completes.

Using addBlocks in TaskUpdate

When you're working on a task and know what depends on it, use addBlocks to declare downstream dependencies. This is useful when you discover dependencies during implementation.

TaskUpdate with addBlocks
TaskUpdate
  id: "html-structure"
  addBlocks: ["css-styling", "js-interactions"]

Result: The html-structure task now blocks both css-styling
and js-interactions. When it completes, both become unblocked.

Example: 3-Phase Feature Implementation

Let's walk through setting up a realistic three-phase feature. We'll create a contact form that requires HTML structure, then CSS styling, then JavaScript validation. Each phase depends on the previous one.

Creating the Task Chain
# Phase 1: Create the HTML structure task
TaskCreate
  id: "contact-form-html"
  title: "Create contact form HTML structure"
  description: "Semantic form with name, email, message fields"

# Phase 2: Create CSS task, blocked by HTML
TaskCreate
  id: "contact-form-css"
  title: "Style the contact form"
  description: "Mobile-first responsive styling"

TaskUpdate
  id: "contact-form-css"
  addBlockedBy: ["contact-form-html"]

# Phase 3: Create JS task, blocked by CSS
TaskCreate
  id: "contact-form-js"
  title: "Add form validation and submission"
  description: "Client-side validation, AJAX submission"

TaskUpdate
  id: "contact-form-js"
  addBlockedBy: ["contact-form-css"]

# Resulting dependency chain:
# contact-form-html -> contact-form-css -> contact-form-js

With this setup, Claude knows the exact order of operations. It will work on HTML first. When HTML completes, CSS automatically unblocks. When CSS completes, JavaScript automatically unblocks. No manual tracking required.

Avoid Circular Dependencies: Be careful not to create loops where Task A blocks Task B and Task B blocks Task A. The system may not prevent this, but it will create tasks that can never be unblocked. Plan your dependency graph before implementing it.

Automatic Unblocking

The real power of task dependencies is automatic unblocking. When a blocking task completes, all tasks it blocks automatically become unblocked. You don't manually update blocked tasks; the system handles it.

When a Task Completes, Dependents Auto-Unblock

Consider our contact form example. When you mark "contact-form-html" as complete, the system automatically checks what it blocks. It finds "contact-form-css" and changes its status from blocked to pending. The CSS task is now ready for work.

Automatic Unblocking in Action
# Before: HTML task is in-progress, CSS is blocked
TaskList
  contact-form-html    in-progress   -
  contact-form-css     blocked       contact-form-html
  contact-form-js      blocked       contact-form-css

# Complete the HTML task
TaskUpdate
  id: "contact-form-html"
  status: "completed"

# After: CSS is automatically unblocked
TaskList
  contact-form-html    completed     -
  contact-form-css     pending       -         <- Now ready!
  contact-form-js      blocked       contact-form-css

How Claude Knows What's Ready to Work On

When Claude reads the task list, it can immediately identify which tasks are available for work. Tasks that are pending (no blockers) or in-progress are candidates. Tasks that are blocked are explicitly off-limits until their dependencies complete.

This visibility helps Claude make intelligent decisions about what to work on next. If asked to "continue working," Claude can pick up the next unblocked task without you specifying which one. The dependency graph guides the workflow.

TaskList Shows Blocked Status

TaskList is your window into the dependency state. Every blocked task shows what's blocking it. This transparency helps you understand project bottlenecks and predict when tasks will become available.

TaskList with Dependencies
TaskList

Active Tasks:
-------------
[t1] Create HTML structure
     Status: in-progress
     Blocks: t2, t3

[t2] Add CSS styling
     Status: blocked
     Blocked by: t1 (Create HTML structure)

[t3] Add JavaScript interactions
     Status: blocked
     Blocked by: t1 (Create HTML structure)

[t4] Create API endpoint
     Status: pending
     (No blockers - ready for work)

Summary: 1 in-progress, 2 blocked, 1 pending
Ready for work: t1, t4
Multiple Blockers: A task can be blocked by multiple tasks. It only unblocks when ALL of its blockers complete. This is useful for tasks that need inputs from several sources before they can start.

Common Patterns

Certain dependency patterns appear repeatedly in software projects. Understanding these patterns helps you structure work effectively.

Sequential Pipeline (A -> B -> C)

The simplest pattern is a linear chain where each task depends on the previous one. This is ideal for phases that must happen in strict order, like build processes or feature implementations that layer on each other.

Sequential Pipeline
Pipeline: Design -> Implement -> Test -> Deploy

TaskCreate id: "design"
TaskCreate id: "implement"
TaskCreate id: "test"
TaskCreate id: "deploy"

TaskUpdate id: "implement" addBlockedBy: ["design"]
TaskUpdate id: "test" addBlockedBy: ["implement"]
TaskUpdate id: "deploy" addBlockedBy: ["test"]

Flow:
design (ready) -> implement (blocked) -> test (blocked) -> deploy (blocked)

As each completes, the next unblocks automatically.

Parallel Then Merge (A + B -> C)

Sometimes tasks can proceed in parallel, but a downstream task needs results from both. This pattern is common when multiple components must integrate, or when separate teams work on pieces that combine later.

Parallel Then Merge
Pattern: Frontend and Backend in parallel, Integration after both

TaskCreate id: "frontend-form"
TaskCreate id: "backend-api"
TaskCreate id: "integration"

# Integration blocked by BOTH frontend and backend
TaskUpdate id: "integration" addBlockedBy: ["frontend-form", "backend-api"]

Flow:
frontend-form (ready)  \
                        -> integration (blocked until BOTH complete)
backend-api (ready)    /

Both frontend and backend can work simultaneously.
Integration only unblocks when both are done.

Approval Gates (Requires Human Sign-off)

Some workflows require human approval before proceeding. You can model this with a task that represents the approval step. Downstream tasks are blocked by the approval task, which only completes when a human marks it done.

Approval Gate
Pattern: Development -> Review Approval -> Production Deploy

TaskCreate id: "develop-feature"
TaskCreate id: "code-review-approval"
  description: "Human must approve after reviewing changes"
TaskCreate id: "deploy-production"

TaskUpdate id: "code-review-approval" addBlockedBy: ["develop-feature"]
TaskUpdate id: "deploy-production" addBlockedBy: ["code-review-approval"]

Flow:
develop-feature -> code-review-approval -> deploy-production
                   (human gate)

Development completes, review becomes available.
Deploy stays blocked until human completes review task.

This pattern prevents automated systems from proceeding past checkpoints that require human judgment. The dependency system enforces the gate; humans control when it opens.

Combining Patterns: Real projects often combine these patterns. You might have a sequential pipeline where some phases contain parallel work that merges, with approval gates at key milestones. The task system supports arbitrary complexity.

Practical Example: Building a Feature

Let's put everything together with a complete example. We'll build a user profile feature with three phases: HTML structure, CSS styling, and JavaScript interactions. Each phase must complete before the next can begin.

Phase 1: Create HTML Structure

Start by creating the foundational task. This has no blockers since it's the first in the chain.

TaskCreate for Phase 1
TaskCreate
  id: "profile-html"
  title: "Create user profile HTML structure"
  description: |
    Build semantic HTML for user profile page:
    - Profile header with avatar and name
    - Bio section with edit capability
    - Settings form with preferences
    - Activity feed placeholder
    Use proper heading hierarchy and ARIA labels.
  status: "in-progress"

Phase 2: Add CSS Styling (Blocked by Phase 1)

Create the styling task and immediately block it by Phase 1. This task exists but cannot start until HTML is complete.

TaskCreate and TaskUpdate for Phase 2
TaskCreate
  id: "profile-css"
  title: "Style the user profile page"
  description: |
    Apply styles to existing profile HTML:
    - Mobile-first responsive layout
    - Avatar with loading state
    - Form styling matching design system
    - Activity feed card styles
    Use CSS custom properties for theming.
  status: "pending"

TaskUpdate
  id: "profile-css"
  addBlockedBy: ["profile-html"]

# profile-css is now blocked, waiting for profile-html

Phase 3: Add JavaScript (Blocked by Phase 2)

Create the JavaScript task and block it by Phase 2. This creates the full chain: HTML -> CSS -> JavaScript.

TaskCreate and TaskUpdate for Phase 3
TaskCreate
  id: "profile-js"
  title: "Add JavaScript interactions to profile"
  description: |
    Implement profile page interactivity:
    - Avatar upload with preview
    - Inline bio editing with save
    - Form validation and submission
    - Activity feed lazy loading
    Handle loading and error states.
  status: "pending"

TaskUpdate
  id: "profile-js"
  addBlockedBy: ["profile-css"]

# profile-js is now blocked, waiting for profile-css

The Complete Workflow

With all tasks created and dependencies set, here's how the workflow proceeds:

Complete Workflow
# Initial state
TaskList
  profile-html    in-progress   -
  profile-css     blocked       profile-html
  profile-js      blocked       profile-css

# Claude completes HTML work
TaskUpdate id: "profile-html" status: "completed"

# CSS automatically unblocks
TaskList
  profile-html    completed     -
  profile-css     pending       -          <- Ready!
  profile-js      blocked       profile-css

# Claude works on CSS, then completes it
TaskUpdate id: "profile-css" status: "completed"

# JavaScript automatically unblocks
TaskList
  profile-html    completed     -
  profile-css     completed     -
  profile-js      pending       -          <- Ready!

# Claude completes JavaScript
TaskUpdate id: "profile-js" status: "completed"

# All phases complete
TaskList
  profile-html    completed     -
  profile-css     completed     -
  profile-js      completed     -
Validate Between Phases: Dependencies handle sequencing, but you should still validate work between phases. When HTML completes, check that the structure is correct before CSS begins. Catching issues early is always cheaper than fixing them later.

Going Further

Task dependencies solve the sequencing problem: ensuring work happens in the right order. But complex projects have another challenge: ensuring the right specialists handle each task.

Dependencies Handle Sequencing

With addBlockedBy and addBlocks, you've mastered the timing of work. Tasks automatically unlock when prerequisites complete. The dependency graph guides the workflow without manual intervention.

But Who Does Each Task Matters

Sequencing is only half the equation. Consider our user profile example: Phase 1 (HTML) should be handled by someone who understands semantic markup. Phase 2 (CSS) requires visual design expertise. Phase 3 (JavaScript) needs programming skills.

In a single-agent setup, Claude handles everything. But as projects scale, you want specialists: a frontend designer for HTML/CSS, a web developer for JavaScript, an iOS developer for Swift code, a Firebase developer for backend logic.

Multi-Agent Development

This is where task dependencies combine with specialist agents for maximum effectiveness. Each phase routes to the right expert. Dependencies ensure work flows in order. The result is coordinated, high-quality output across all aspects of a project.

Dependencies + Specialists
Task: profile-html
  Agent: frontend-designer
  Status: in-progress
  Blocks: profile-css

Task: profile-css
  Agent: frontend-designer
  Status: blocked
  Blocked by: profile-html
  Blocks: profile-js

Task: profile-js
  Agent: web-developer
  Status: blocked
  Blocked by: profile-css

Each task goes to the right specialist.
Dependencies ensure correct sequencing.
The PM coordinates the handoffs.

Dependencies tell you WHEN work can happen. Specialist agents tell you WHO should do it. Together, they enable sophisticated project coordination that scales to complex, multi-platform development.

Ready for Multi-Agent Development?

Claude Architect provides specialist agents (frontend, backend, iOS, web) coordinated by a PM agent. Task dependencies work seamlessly with agent routing for production-grade project management.

Join the Waitlist