In this document, we will outline a branching and merging strategy built atop Git for source control, with specific attention paid towards the goals of quality, releasability and ease of development.
While there are many version control solutions available, Git has solidified its place as a leader both in open source development as well as the enterprise through solutions such as GitHub, Bitbucket, and GitLab. Git’s support for lightweight branches and its distributed approach allows for workflows that simply could not be supported in previous generation version control solutions.
This branching strategy is an enhancement of GitFlow which was initially documented by Vincent Driessen at nvie. It is a strategy for Git to support simultaneous feature development, bug fix development and release management in large codebases shared by multiple developers. Prior to the adoption of Git, the branching model utilized in GitfFlow was not possible because it relied on the ability to merge between branches at any time. For instance, if a bug is found during regression, it can be fixed and merged into the Release, and then merged into Work Branches with little concern of conflicts.
Let’s take a look at the standard GitFlow Branching Model and process.
In this model, there are two main branches, Master and Develop, which constitute the main trunk of the repository. The Develop Branch is for ongoing, active development while the Master is for released code only. The Master Branch will in fact contain very few commits, which should only contain tagged, released code. That same code will also be merged to Develop to keep it up to date with Master. In order to work, developers will create branches from Develop or Release, depending on if they are working on a new Feature or a Release Bug.
Features are implemented through User Stories, and GitFlow advocates creating a Work Branch for every single User Story. It is recommended that the ticket ID is used as the prefix for the branch name with a descriptive title for the work to be done. For example, for a User Story with the ID of PK-427 that details the creation of a new user account form, the developer would create a branch from Develop called ‘PK-427-user-account-form.’ As work proceeds, the developer will commit often to their Work Branch, and once complete and approved, the branch is merged back to Develop.
Merging & Pull Requests
While GitFlow does not dictate a specific strategy for merging Work Branches, we recommend using a Pull Request workflow. Instead of allowing developers to directly merge the code in their completed Work Branch into Develop, they create a Pull Request which initiates a Code Review process for other developers to review the work completed. One or more developers will review the new code in its entirety, leaving comments and suggestions for improvements, changes, refactoring, or code cleanup. Once the review is complete and all updates have been committed, the Reviewer will accept the Pull Request, which merges the code back into the Develop Branch.
It is important to understand that no code is ever merged without the Pull Request process, and the developer who opened the Pull Request will never merge their own code. The Pull Request and Code Review process is meant to encourage collaboration and knowledge share, which we have consistently seen lead to a more stable, higher quality code base.
When it’s time to release the system, a new Release Branch is created from Develop, and the Regression process begins. Developers will create new branches from Release for every defect found. Once a defect is fixed, it will follow the Pull Request process to merge the fix branch to Release. Once the Release Branch has been validated by both QA and business, it is tagged and merged to both Master and Develop.
In this manner, Master will contain only the released, tagged code, and Develop will always be kept up to date with the Master. At any time, Developers can merge Develop into their own Work Branches in order to keep them up to date.
Continuous Delivery & GitFlow
GitFlow, enhanced with Pull Requests and Code Reviews is increasingly popular in the software industry, but falls short in one key area. Every time a release is prepared, it is created from the active Develop Branch, which means all features that have been completed and merged and must be accepted in order to release. This often makes it difficult to plan for a release, as Business must find the optimum time when all approved features are complete. Regression cycles are also often lengthy as QA needs to validate all features delivered to date at once. This is often the first time multiple features have been heavily tested together, and naturally a number of bugs will be discovered throughout the process.
In order to support on demand, Continuous Delivery, we must enhance this process in order to satisfy the following requirements:
- All Features must be code complete, bug free and approved by Business before being merged with other Features.
- Business must be able to approve or disapprove any completed Features prior to creating a Release Branch.
- Regression and testing time must be minimized as much as possible, in order to ensure fast release cycles.
- The Release Branch must be kept as stable and bug free as possible, so it can be released at any time.
Let’s take a look at how we can modify the GitFlow Branching Strategy to satisfy some of these goals. The largest issue in GitFlow that keeps it from satisfying the above requirements is the Develop Branch. Traditionally, as developers complete User Stories for different Features, they merge the Work Branches back into Develop and move on to a new story. The problem is that Develop continues to gain code for Features that are not complete. Some User Stories are finished and merged, but others are still waiting to be started. When multiple Features are being developed at once, this problem compounds. We must wait for all User Stories and Features in development to be completed and merged in order to begin the Release process. We need a way to develop each Feature in isolation so that it is safe to complete individual User Stories without compromising the common code base.
As you see outlined in the diagram below, we have gotten rid of the Develop Branch entirely, and replaced it with a number of Feature Branches to develop in isolation.
Every time a Feature is started, the latest code in Master is used as a branch point for the new Feature. Starting from Master ensures that the Feature being developed will have no dependencies on unreleased code. As developers work on the User Stories for a specific Feature, they create Work Branches, as before, but this time from a specific Feature Branch. Once all the User Stories are complete for the Feature, it can be reviewed by Business and QA on it’s own branch. If any bugs are found when reviewing the Feature, they will be fixed and merged back into the Feature Branch before being accepted for Release.
By isolating Features this way, we ensure that no code is merged into a central location until it is 100% complete. We’ve also created a process for approval, as Business can review builds of a Feature Branch to ensure they meet the success criteria for that Feature. Any defects, missed Acceptance Criteria, or changes based on Business feedback can be fixed prior to approval. With proper devops in place, we can also provide QA with the ability to build and test each Feature Branch, shifting much of the testing process from the Regression to Development, ensuring the quality of delivered code, and significantly reducing the Regression Cycle during release.
We have also allowed for more flexibility in the release process, as we do not need to wait for any Feature to be finished in order to create a Release Branch. If a Feature is complete and approved, it can be merged to Release. If not, it simply waits for the next Release. This requires Business to shift mindsets into a Continuous Release Process, where as they always have a stable Release Branch ready, and can choose what Features they want to include based on completion, stability, and if the Feature satisfies the business ask for which it was designed.