Task Branch- Updated
An update to an earlier post based about using branches to manage work on an agile team. A Task Branch helps keep an active Main Line.
You need place work that facilitates the use of feedback mechanisms such as testing and Code Review to help you merge with confidence so that you can have consistently working code on the Main Line. This pattern describes a codeline structure that enables your team to work in parallel to deliver multiple changes to the Main Line quickly while preserving the integrity of the Main Line and maintaining focus.
Balancing Isolation and Collaboration
A typical development workflow includes the following steps:
Pulling the current state of the code from the Main Line.
Making changes to the code.
Getting feedback on the change from team members and/or tools
Merging the work into the Main Line.
It’s typical to code and test in your private workspace before sharing your changes with the rest of the team. Unless you work in a communal workspace(which would be challenging), there is always a period of time when your changes are isolated from the Main Line. While this means that your work isn’t always up to date, having no isolation can become chaotic as the number of contributors increases.
A workspace, with or without a local branch, is a parallel work stream, much like a shared branch, but without the transparency and automation support branching enables.
There are a few options for managing this isolated work stream. Each option differs in:
How visible it is to other team members
The ability to use the mechanisms of your version management system to have more freedom to experiment.
How much you can leverage your shared CI workflow.
A good solution will allow you to work in a way that yields the benefits of (brief) isolation while minimizing the time until the code is merged into the Main Line.
Some of the ways you can manage the code in your workspace before integrating with the Main Line are:
Push directly to the Main Line from your workspace.
Create a branch locally that you don’t push, and push the result to the Main Line
Create a branch for your work and push it to the shared repository. When complete, merge this branch to the Main Line (directly or via a Pull Request).
A good solution will allow you to work in a way that yields the benefits of (brief) isolation while minimizing the time until the code is merged into the Main Line.
A workspace without a backing branch is straightforward to manage because it requires fewer interactions with the SCM tool. However, you cannot easily track steps during the coding process. Some of the disadvantages of this approach are:
Limited ability to get feedback using the tools available in a Continuous Integration Environment before you merge
Making it more complicated to get input from team members who are not co-located (temporally or geographically) because your work-in-progress code is invisible to other team members
An increased possibility of losing work in progress due to the lack of version and tooling support in a shared repository
A workspace with a local branch lets you track changes locally and experiment easily (see the Private Versions Pattern from SCM Patterns). However, this approach has the same issues relating to lack of transparency and tooling support workspace-only approach.
Using a branch, you can collaborate on a feature with other developers and get the advantage of quickly testing the code in the branch in a Continuous Integration Environment, opening up more potential for automation, information sharing, and collaboration without the right expectations. However, working on a branch can encourage slower, asynchronous interactions.
Using branches can cause problems when teams encourage working on a long-lived branch—such as a Feature Branch—until work is complete. While this seems to offer some superficial advantages, especially if the work is isolated from the rest of the code base, the cost of a longer gap between integration easily outweighs any potential advantage. The longer the delay, the higher the risk of merge conflicts and errors.
You want to enable collaboration and reliable testing while also working to minimize the length of time between starting work and merging, which includes:
Time between work starting and code being completed.
This depends on the developer’s skill and speed, and the task size
Time between work being complete and merging
This depends on the dynamics of the feedback cycle
Branch for Tasks, Merging Quickly
Therefore:
For each development task, work off a Task Branch. A Task Branch represents a small coherent unit of work that can be done reasonably quickly. Merge into the Main Line as quickly as possible.
Have a backing store for work in a workspace related to a Small Development Task.
Maintain flow.
Experiment
Obtain Feedback
Using short-lived Task Branches is consistent with rapid integration to a Main Line. In Accelerate the authors say:
Following our principle of working in small batches and building quality in, high- performing teams keep branches short-lived (less than one day’s work) and integrate them into trunk/master frequently.
A “Task”, which is described in more detail in Small Development Task can be a User Story or an intermediate step for a user story. The main attribute is that it is a small, coherent unit of work. Small can vary by team, but a typical goal is to be able to integrate at least daily.
Example
A Task Branch follows a familiar workflow:
Check out the Main Line
Create a Task Branch
Make code changes, including tests. This can include multiple commits
Push changes to the shared repository periodically.
Get feedback.
Merge the completed work.
Delete the Task Branch
The goal is to integrate the work into the Main Linequickly.
Cautions
Don’t confuse a Task Branch with a Feature Branch. A Task Branch is shorter and allows for incremental work. A Feature Branch (rarely a useful pattern to follow) often represents a larger unit of work that survives until the “feature” is complete, at which point the code merges to the main line.
Be mindful of:
Merge Conflicts: To minimize the risk of merge conflicts causing delays at the end, pull from the Main Lineperiodically to simplify later merges and identify possible design divergence early.
Task Branches that last a long time. Gather data (either metrics or heuristics) to identify when Task Branches take a long time to merge. Discuss these at Retrospective to evaluate if the long branches were problematic and, if so, how to fix the underlying issues.
Overly restrictive Codeline Policies for the mainline that require a slow Code Review process.
Using a Task Branch can make these problems more obvious (since the SCM tooling makes the parallel work stream visible), but that doesn’t mean that the branching policy is the cause of the problem. These issues can also manifest when doing a no-branch directly from the workspace workflow. The causes of long integration times are often related to problems external to the code line flow, such as planning and prioritization.
Aside: Branch Reuse
One approach to balancing the overhead of branch creation with frequent integration is to create one branch for a larger task, but merge multiple times during the branch’s lifetime. If your policy if to delete the (remote) branch after a merge, this is conceptually the same as multiple task branches. The team should decided if there is any value to creating uniquely named branches for each merge. In many cases, the “multiple merges” approach can work fine if the team finds it easier.
Next Steps
To integrate a task branch quickly while minimizing the risk of errors, you need to:
Ensure that tasks are the right size to complete quickly and well-defined enough to know when they are done. Identify Small Development Tasks that support completing work quickly.
Have a Code Review process to support shared understanding and identify likely errors.
Use an Integration Build in a CI environment to ensure that a consistent set of checks is run on the code.