Skip to main content

Overview

Task breakdown is the practice of splitting a feature into small units of work before implementation. Each task aims to merge on its own, revert on its own, and drag no unrelated changes with it. The preparation done before writing code determines pull request granularity: building PRs from the broken-down tasks lets you keep creating PRs at the right granularity.

Benefits

Listing and splitting tasks before starting looks like a detour, but across the development cycle it turns out to be the fastest path.

More accurate estimates

Small, concrete tasks are easier to size than one large feature, so the total estimate carries fewer surprises.

Early agreement on the approach

Writing the task list in an issue and having it reviewed before implementation surfaces missing cases and wrong assumptions while they are still cheap to fix, which cuts rework.

PR granularity stays right

Building PRs from the broken-down tasks keeps each PR focused on one thing and prevents it from bloating.

Visible progress, easy hand-off

A checklist of tasks makes status legible at a glance and makes handing work between people — or between people and AI agents — straightforward.
Good task breakdown also translates into providing AI agents with the right context. Context engineering is the practice of preparing the context an AI agent works with — instructions, reference material, and scope — so that it contains exactly what the goal requires, no more and no less. A task with uniform change content lets you load only the information that piece of work needs, so the agent concentrates its limited context on one thing. Task breakdown, pull request granularity, and context engineering are tightly linked: breakdown decides the unit of work, that unit drives the granularity of the PR, and that granularity determines the quality of the context the agent receives. Even when implementation is delegated to AI agents, the starting point is a good breakdown.

What makes a good task

A well-sized task satisfies four conditions at once.

Merges without breaking anything

The change is safe to integrate on its own, and the application keeps working after the merge.

Includes its tests

Implementation and the tests that prove it ship in the same task, never separately.

Reverts in isolation

Rolling it back does not disturb other tasks.

Reviewable in one sitting

It stays small enough that a reviewer can read it through without breaks and understand the whole change.

Keep together or split apart

Cohesion decides the boundary. To judge whether two pieces of work belong to the same task, apply three checks in order. If any answer forces them together, keep them together.
1

Check independence

Does task B work correctly without task A? If not, they are one task.
2

Check completeness

Is task A a meaningful, mergeable unit on its own? If not, merge it into the task that gives it meaning.
3

Check revert impact

Does reverting task A also break task B? If so, they are one task.
Some combinations do not need the checks — they always land the same way.

Always keep together

  • Implementation and its tests
  • An API endpoint and its routing
  • A data model and its migration

Always split apart

  • Refactoring from new features
  • A library upgrade from feature work
  • A performance change from feature work
  • A data migration from feature work
  • Each stage of a feature flag (add → enable → remove)
  • Features that do not depend on each other

Breakdown steps

Breakdown is iterative. The first list does not need to be perfect; it gets more precise as more people look at it and as the breakdown progresses. This section walks through building a task list, using the task “Add a REST API that returns a list of users” as an example.
1

Draft a task list from the requirement

Start by writing the rough requirement as an issue task list. A single top-level line is enough at this point.
2

Expand into independently shippable slices

Break the single line into pieces that ship on their own. Fetching the list, filtering, pagination, and sorting each became their own task. Build the feature by stacking small additions instead of implementing everything at once.
3

Review the list across roles

Have frontend, backend, and product reviewers look at the list before any code is written. Discussion turns vague items concrete: in this example, aligning in review pinned the name filter down to a partial match.
4

Keep splitting during implementation

When implementation reveals reality, feed it back into the list and split further. In this example, writing tests surfaced the hidden requirement of handling deactivated users, and the fetch task split into “return all users” and “exclude deactivated users”. Keep updating and splitting the list during implementation instead of letting one task swell.

Dependencies and parallelism

Once the list settles, map the dependencies between tasks and identify which tasks can run in parallel. Tasks with no dependency on each other can be implemented simultaneously — by different people, or by multiple AI agents. For example, a bulk user registration feature breaks into five tasks: the domain model comes first, the parser and the validator depend only on it and run in parallel, the service layer combines them, and the API endpoint sits on top.
Mapping the dependencies of this task list produces the following diagram.

Turning tasks into issues

Once the breakdown and its dependencies are settled, create an issue per task. Checkboxes in a task list cannot track assignees, discussion, or progress per task. Promoting a task you are about to start into its own issue makes one issue one unit of work, with the requirements and discussion for that task collected in one place. Structure the feature and its tasks as parent and child issues.
  • Parent issue — represents the whole feature. It keeps the background, the goal, and the full task list, and links to each child issue so progress can be followed from one place.
  • Child issue — represents one broken-down task. It states the task’s requirements and completion criteria, and records the dependencies mapped earlier. If the tool supports linking parent and child issues, the children’s progress rolls up to the parent.
On GitHub, this structure maps to two features: sub-issues and issue dependencies. Sub-issues carry the parent–child relationship — which feature a task belongs to — while dependencies carry the starting order — which task to start after. Sub-issues (parent–child) — adding the child issues to the parent as sub-issues makes the feature-task hierarchy explicit, and the children’s completion rolls up to the parent as progress. See Adding sub-issues in the GitHub Docs for the steps. For the bulk registration feature, the five child issues hang under the parent issue.

Parent issue: Add bulk user registration

Sub-issues (0 / 5)
  • Task 1: Define the domain model
  • Task 2: Implement the parser
  • Task 3: Implement the validator
  • Task 4: Implement the service layer
  • Task 5: Add the API endpoint
Dependencies (starting order) — record the starting order between child issues as issue dependencies. Setting a dependency on Task 1 for the Task 2 issue makes it visible, even in lists, that Task 2 cannot start until Task 1 completes, and it can replace writing dependencies in the issue body. See Creating issue dependencies in the GitHub Docs for the steps. The dependencies of the bulk registration feature form the following graph, where each arrow points to the issue it depends on — the one that must complete first. Start with Task 1, which has no dependencies; Task 2 and Task 3 share the same dependency and can proceed in parallel. An issue created this way is a unit of work you can hand to a person or an AI agent as is. With the requirements, completion criteria, and dependencies in one place, the quality of the context the implementer receives is largely decided at this point. Issues with no dependencies can be started in parallel.