April 12, 2023
9
min read

Feature Branch Concepts and Best Practices

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.

Summary of key feature branch concepts

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.

Single-developer adoption A breakdown of how a single developer would create, contribute to, and ultimately merge a feature branch back into a target integration branch
Team adoption Highlights how a team (multiple developers, QA team members, project managers, etc.) might leverage code contribution into a feature branch
Development team benefits An overview of how feature branches enable greater flexibility over the alternative, as features may progress at different paces relative to each other.
Quality benefits How feature branches enable teams to achieve improved code and product quality in the target integration branch
Challenges Efficient communication is of paramount importance to ensuring that work on feature branches is completed smoothly. This is essential not only to keep all contributors aligned in their responsibilities but also to help resolve merge conflicts before merging back into the target integration branch.
Best practices How the benefits of feature branches may be further increased as a result of some common best practices.

Single-developer adoption

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.

Figure 1: A single developer creating a feature branch, committing to the feature branch, and merging back into the target integration branch (main in this example)

A standard Git flow to create such a branch from a command line would be as follows (“main” used in example):

# Switch to target integration branch
git checkout main

# Local branch fully up to date
git pull

# Create a new feature branch
git checkout -b feat123_someNewFeature

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).

# Set commit message for the feature that is not yet fully complete
git commit -m “Initial changes for the feature”

… additional code changes to complete the feature …

# Addition of another commit message for remaining code changes. Feature is
# done, but pre-merge criteria are not yet met
git commit -m “More changes for the feature”

# … additional code changes needed to meet pre-merge criteria …

# Last commit, pre-merge criteria met
git commit -m “Additional unit tests added for new code”

# Switch back to the target integration branch
git checkout main

# Pull remote changes to ensure that local integration branch is up-to-date
git pull main

# Merge feature branch into target integration branch
git merge feat123_someNewFeature

# Push local target integration branch (now with feature branch) to the
# remote repository
git push

# Delete feature branch
git br -d feat123_someNewFeature

Team adoption

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.

Figure 2: Multiple members of a team contribute to a feature branch much as a single developer would

Environments as a Service (EaaS)
Platform
Qovery
Release Hub
Uffizzi
Lightweight and Fast (All-Container Solution)
Easy Setup
(Based on Docker Compose)
Reusable Github Actions Workflow

Cost
$$$
$$$$
$
See Comparison Table
Platform
Lightweight and Fast (All-Container Solution)
Easy Setup
(Based on Docker Compose)
Reusable Github Actions Workflow
Cost
Qovery
$$$
Release Hub
$$$$
Uffizzi
$
See Comparison Table

Development team benefits

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.

Quality benefits

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.

  • Best practice: Successful code compilation is a pre-merge requirement before any code can be merged from a feature branch into the target integration branch. If compilation of the feature branch code fails, the code is iterated on until compilation completes successfully.
  • Quality benefit: Code within the target integration branch is always guaranteed to compile successfully.

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.

  • Best practice: Automated test cases must be run against code in feature branches, and those test cases must complete successfully before the code is eligible to be merged into a target integration branch.
  • Quality benefit: No bugs surfaced by the automated test cases of the legacy feature will be in the target integration branch.
See how Spotify packs more into every release with ephemeral environments
Read Case Study

Challenges

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:

  • Let test automation document new or changed functionality. Well-authored tests serve as an excellent solution for communicating changes to anyone reading them in the future. They also ensure that any new functionality does not regress over time as future changes to the product are made.
  • Write persistent documentation. This documentation can be referenced by any member of the team. This can be done both when the code changes are introduced and also well into the future whenever questions arise regarding how a feature or set of changes is expected to behave or how any newly added code should be interfaced with.

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.

Figure 3: Rebasing the feature branch against the main branch, adding the feature branch commits on top of the most recent commits to the main branch

Best practices

There are several best practices that can be followed to further increase the inherent benefits to a development team from working with feature branches:

  • Include test automation in pre-merge criteria. A team is free to choose whatever pre-merge criteria make the most sense. These could include simply ensuring that a set of code changes compiles, performing peer reviews, or running fully automated test suites and receiving product manager signoff. More pre-merge quality gates will result in higher-quality code ultimately being merged into the target integration branch.
  • Write new tests for new code. The execution of existing test automation (above) is a great best practice in itself. However, new code changes will not benefit from this best practice unless new tests are authored as these changes are introduced. A new feature (feature #1) can be guaranteed to maintain the quality of a legacy feature by ensuring that test cases validating that legacy feature execute successfully. Without new test cases explicitly validating the desired functionality of feature #1, a subsequent feature (feature #2) could very easily introduce new bugs and lower the quality of feature #1. But if new automated tests validating feature #1 were added at the time of development, feature #1 may realize the same quality benefit of legacy features, as the new automated test cases must pass before subsequent features can be merged.
  • Limit the scope and duration of feature branches. Short-lived feature branches require fewer rebasing efforts, which means there are fewer merge conflicts to address than feature branches that diverge from the target integration branch for significant periods. Keeping the work focused on a particular goal or task and then merging back into the target integration branch helps minimize the feature branch’s lifespan.
  • Use ephemeral environments for significantly greater integration testing capabilities pre-merge. Many common pre-merge criteria can be satisfied via short-lived tasks performed on a single-build machine. Examples include code compilation, the ability of a service to start locally, ensuring that there are no merge conflicts, and obtaining pull request approvals. Full-stack ephemeral environments allow for greater integration testing among multiple components of the system and exploratory or acceptance testing by stakeholders (product managers, quality team members) without many of the costs that come with creating, maintaining, and monitoring persistent shared test environments.

Conclusion

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!

Uffizzi logo
Environments as a Service
Learn More
preview icon
Empower your devs with an environment for every pull request
time icon
Avoid release delays that cost millions in missed revenue
Velocity icon
Improve development velocity at scale by up to 50%
Lifecycle icon
Plan capacity in real time with lifecycle management built in
Learn More