Product Backlog Items, even when sized to fit in a development iteration might still be too large to let you maintain the integration cadence you want. You want to integrate each Task Branch quickly. This pattern describes a way to approach splitting a backlog item into development tasks to keep the work on the task branch short and code robust?
Working in Increments Can Be Hard
You want to integrate changes frequently to maintain a healthy Main Line. While the definition of “frequently” can vary by team and context, for many teams, it means “at least once a day.”
While a Product Backlog Item could be small enough to be completed within your desired integration time, in many cases, a PBI will involve more work than that.
It can sometimes be difficult to identify units of work related to a backlog item that reflect useful, “ready to merge” functionality:
Smaller units of work may not reflect complete functionality but are quicker to review and integrate.
A larger task might provide some end-user value but will take longer to complete and review.
You want to integrate consistent units of work that won’t break existing functionality or reveal partial features unexpectedly.
Even when teams are cross-functional and include “full stack” developers, some stories might require work from multiple team members. For example, a story with mobile, web, and backend components might involve three developers. Coupling development tasks adds schedule and integration risk, but decoupling the tasks can involve disposable scaffolding. Integrating a partial implementation of a Product Backlog Item can enable integration and collaboration between developers working on the Product Backlog Items.
Smaller, frequent merges help to avoid integration surprises, but each merge to the Main Line adds overhead, particularly for Code Review. When tasks are coupled, each task adds coordination overhead. When tasks are larger, dependencies can slow development.
When you develop using Test-Driven Development and/or Unit Testing, the tests typically cover small units of work, even if the units don’t represent a complete feature.
You want to balance delivery speed with process overhead, giving you the ability to deliver working software at high frequency,
Small Pieces Loosely Joined
Split Each Product Backlog Item into Small Development Tasks that can be completed and integrated in a day or less. Design to allow for incremental integration and/or feature hiding.
Small Development Tasks are coherent units of work that can be completed quickly and merged into the Main Line without breaking anything. A Small Development Task is usually associated with a Task Branch. If a Product Backlog Item cannot be completed by a developer in the desired integration time, plan to break it into a set of tasks that:
Are completable in a day or less by a developer (or pair)
Have associated Unit Tests.
Can be integrated into the main line without breaking existing functionality or creating risk.
The team should plan these tasks in a way that facilitates collaboration. While, Ideally, Product Backlog Items are independent, Small Development Tasks may have sequence dependencies, though strive to develop mechanisms such as contract tests and interface definitions that allow others to move forward while a task is in progress,.
By breaking down work into smaller parts, you have the ability to:
Evaluate your progress in a definitive way. as it is often easier to define "done" for a smaller task.
Get feedback from your colleagues before you dive into a problem.
Share effort if any of the work can be done in parallel.
Simplify updates and merges, as the changes to the Codeline will all be small at any time.
Planning for Small Development Tasks means that your team need to do some basic design work during planning and identify intermediate goals, which will provide more confidence in their ability to meet goals.
Task Breakdown Approaches
Breaking down work need not be complex, though doing it well requires some design thought. Some natural boundaries in the development process can help you break down tasks:
Consider what tests you write as (or before) you code. Each passing unit test could be a commit point, and the complete test suite could be an integration point.
Decide what you want to accomplish before leaving work each day so that you end the day with a sense of accomplishment.
Identify interfaces that you could stub out to get to a commit point more quickly; the first task might be to create the implementation skeleton, and filling in each stub with a real implementation could be an additional task.
For example consider an app that has mobile, web, and backend components with complicated backend processing. You could:
Build web and mobile interfaces using stubbed data that meets the contract with the backend (this tests the utility of the data and user interfaces)
Implement a stubbed endpoint that delivers stub data (perhaps with some variation). This lets clients test connectivity and interactions. The clients can then verify their integration with simple end-to-end tests.
Build the real backend endpoint that includes, for example, database changes. At this point, you will have something that you can demonstrate to a stakeholder with realistic functionality,
Not all post-merge integrations will go smoothly, especially if the intermediate tests were incomplete, but you can work to improve on this.
When you have a small task that implements only part of a feature, you want to be mindful that the work does not interfere with the running system. There are (at least) 3 basic strategies:
“Private Access” or “Bottom Up”: Write code that is not accessible to external users of the system. For example, add a service layer that you can unit test or access by upstream clients but don’t expose to web handlers and other external interfaces. This makes more sense when adding new code than when changing existing code.
User Versioned Interfaces
Hide the code: Using a Feature Flag or similar mechanism so that the new code paths are accessible to code running with the proper configuration.
Add test cases that ensure that code is not accessible from an external interface unless the correct flags are set. Be sure to apply appropriate security approaches to ensure that partial implementations do not expose vulnerabilities.
Cautions
While smaller, independent tasks are good, be mindful of tasks so small as to cause churn.
In some cases, it’s better to start building than over-designing interfaces upfront.
Delivering in increments of Small Development Tasks might seem slower at the start until the team develops a new mindset and testing and integration frameworks to support this approach.
Next Steps
Defining what’s done: Unit Test
Limiting the visibility of work that isn’t ready for users: Feature Flag
Getting Started
While Small Development Tasks are a great way to facilitate incremental integration across layers that might involve different skill sets, you can also start with any backlog item that might take more than a day to do. Consider starting by defining tasks that rely on mocks or stubs of more complicated system elements so that you can get a skeleton of an end to end system developed quickly.