Learn how to create and merge a feature branch in a software development environment, including benefits, challenges, and best practices.
Contributing code to a feature branch is a software development practice that aims to keep the quality of an integration branch stable as new code changes are introduced. This is achieved by committing new code contributions into a divergent feature branch in complete isolation from the target integration branch.
It is acceptable for the quality and completeness of the feature branch to vary during development. However, only when the changes have been stabilized and the predetermined merge criteria have been met should a feature branch be merged back into the integration branch and deleted. Properly adopted, the measurable quality of an integration branch should not decrease over time since pre-merge criteria would require quality metrics to be met before any feature branches are merged.
This article provides an overview of how feature branches can be used within a software development team, details about the benefits that come with their adoption, challenges that a team may face with this practice, and a few best practices that a team can adopt to maximize the return of developing using feature branches.
A single developer who begins work on a new set of changes (feature, user story, refactoring, etc.) creates a new branch from the target integration branch (Step 1 in the diagram below). This target branch may vary from team to team depending on individual requirements and team release processes but is frequently a “main” or “integration” branch.
A standard Git flow to create such a branch from a command line would be as follows (“main” used in example):
The developer is then free to make any commits in any state to the feature branch while progressing toward completing the work (Step 2 in the diagram). Once development work is completed, the code in the feature branch is assessed against pre-merge criteria to determine merge readiness (see basic pre-merge criteria examples in the “Quality benefits” section below). If the pre-merge criteria have not yet been satisfied (Step 3), the code within the feature branch is improved via further commits, and the pre-merge criteria are reassessed. This process is repeated until the criteria have been met (Step 4), at which point the feature branch is merged back into the target integration branch (main in this example) and the feature branch is then deleted (Step 5).
Usage of and contribution to a feature branch is not limited to individual code contributors. The implementation could be completed by one or more developers; tests could be written by those same developers, a subset of them, or by a unique set of team members altogether (such as test engineers); and part of the pre-merge criteria could include approval from a non-technical member of the team (like a product manager).
Note that the fundamental characteristic of feature branches is the same when leveraged either by individuals or teams: All new changes are kept isolated from the target integration branch until the point at which the work has been completed and pre-merge criteria have been met. The success of this practice requires efficient communication so that each contributor is well aware of the progress and responsibilities of the others. An example of what a team working together on a feature branch might look like can be seen below.
Leveraging feature branches during development provides greater flexibility to a development team. Features vary in size, scope, and priority, all of which contribute to the work being completed at different speeds and on different timelines. By keeping the feature branch code contributions isolated outside the target integration branch until finished, that feature branch may progress at whatever rate is appropriate for the team/project without impacting the completed code that is within the target integration branch.
In a practical example, one development team could begin work on one feature at the same time that a second team begins work on a different feature. These features are of no risk to the code quality within the target integration branch, as the feature work is kept isolated until finished. Whichever feature is completed first can be merged into the target integration branch and is then eligible for release consideration without the need to wait for the second feature to be completed. The second feature may progress at its own pace, to be merged into the target integration branch only when it is ready.
Similarly, work on one feature could be deprioritized relative to another feature. In this scenario, the original feature could be completely paused to allow the team to focus on the second one. Work on the second feature could be started, completed, and ultimately merged back into the target integration branch without affecting, or being affected by, the first feature that was under development but was paused.
In addition to greater timeline and completion-rate flexibility, feature branches also allow multiple team members to collaborate on code changes in a code branch that is not at risk of impacting the rest of the product. The team adoption workflow is highlighted in the section above, but the specific team benefit here is that this team collaboration can enable the work within a feature branch to mature toward completion without affecting the overall maturity of the code within the target integration branch. Pull request feedback might be provided, requiring several changes before completion; quality engineers might identify bugs that need to be addressed; or product managers might adjust requirements after seeing a demo. The in-progress feature branch is kept isolated until all team members have collaborated and finished their respective tasks.
Developing within feature branches provides numerous quality benefits to the code that will ultimately be released (the target integration branch).
Pre-merge criteria that include quality gates ensure that the new code introduced to the target integration branch meets, at a minimum, those quality requirements. Static code analysis, unit tests, and manual or automated integration tests can be run against code deployed to an ephemeral environment, enabling feature branch owners to ensure that high-quality code is consistently being merged to the integration branch.
An ephemeral environment is a short-lived test environment that is created with the sole intention of validating the code within a feature branch prior to being merged back into an integration branch. If leveraged properly, the use of ephemeral environments dramatically increases the ability to validate the quality of new changes. New UI changes are previewable, and data flow through multiple components may be examined and validated without the need to wait for the code to be merged into an integration branch.
Quality benefits should be measurable and demonstrable. The two scenarios below serve as examples of how quality benefits may be realized by leveraging feature branches. These scenarios include basic best practices but are in this section to demonstrate how feature branches can improve the quality of target integration branches.
Scenario #1: The code within a “main” branch occasionally fails to compile after new code changes are merged in.
Scenario #2: Automated test cases exist for a legacy feature, but those automated test cases are not executed until after contributions are made to a feature branch. Bugs are often found when executing these test cases, preventing the release of the new code until the bugs are fixed and the tests pass successfully.
There are a few common challenges that need to be managed when developing within a feature branch and then merging into a target integration branch. When code changes are finally merged back into the target integration branch, the rest of the team needs to be fully aware of how those changes affect the product and the rest of the code in the integration branch. Other developers will need to know if there were any changes to frameworks or design patterns, significant refactors, or if the new features meaningfully impact any existing features.
The following are two ways that this communication gap may be addressed:
When finally merging code back into an integration branch, merge conflicts can often arise. This situation occurs when the set of code changes within a feature branch overlaps with code changes to similar files that have happened since the feature branch diverged from the integration branch. At the point in time when the feature branch is to be merged back to the integration branch, the developer will need to resolve these merge conflicts before merging.
The most straightforward practice to minimize merge conflicts is to frequently “rebase” the feature branch from the target integration branch. This will apply any new commits from the target integration branch to the feature branch. The advantage of doing this regularly is that the code within the feature branch will be compared against the most up-to-date point of the target integration branch, minimizing the chances that multiple commits could modify an identical file and result in a merge conflict.
There are several best practices that can be followed to further increase the inherent benefits to a development team from working with feature branches:
Developing within feature branches before merging into a target integration branch introduces benefits to individual developers, multiple developers, and multi-disciplinary teams. The primary benefit is the enhanced overall quality of the code that is ultimately merged back into the feature branch, which is made possible by ensuring that code changes meet agreed-upon pre-merge criteria before they are merged. The explanations above will help you understand a few of the common challenges associated with feature branches, and the best practices outlined will enable the team to increase the potential benefits of adopting this development practice. Good luck!