A branching strategy is the methodology employed by a developer or group of developers for using the same source control repository. A solid branching strategy can mean the difference between a smooth, seconds-long integration of a new feature and several hours spent in frustration untangling a horrific mess of merge conflicts.
Several clients recently engaged our team at RightBrain Networks to organize their internal development processes. Here’s how we implemented our branching strategy and why it worked for us.
Having a good branching strategy for source control is vital for a quick and efficient development process. Ideally your branching strategy will accomplish the following three goals:
1. Minimize conflicts when merging code.
This removes roadblocks impeding new features or bug fixes from getting to the testing process and saves time on integrating new features or bug fixes. Reducing merge conflicts also reduces the amount of manual intervention needed and increases the likelihood that the fix is less complex when merge conflicts do arise.
2. Track code changes in the development pipeline.
This enables the developers to more easily identify where/when a bug was introduced and also to quickly determine how far along a new feature or bug fix is in the development process (if someone needs to know if a new feature is currently in testing yet, for example).
3. Add a degree of separation between code at different stages of development.
This allows each stage to be isolated from the others, which helps prevent new code changes from moving into the next stage prematurely and disrupting the development process. For example, if we are executing tests on a branch while new features are constantly being merged in, there is an increased chance that an undetected bug may be introduced and pushed to the next stage.
Then Git Busy
At RightBrain Networks we use Git as our source control system. While the examples below use Git commands, you can execute the same strategy using other source control systems, like Mercurial.
When initializing a Git repository, by default the first branch is automatically created with the name “master.” Many developers use this to represent the “stable” branch of the project, which is deployed to production environments or delivered to a client. However, having only one master branch can be limiting when utilizing multiple environments. I recommend that the master be separated into four different branches: develop, test, stage and production.
Develop: This is the main development branch. New features and bug fixes are frequently pushed to this branch. It will be the branch that is updated the most often. When a set of new features and bug fixes are ready to be tested, they are moved into the test branch.
Test: This branch represents the testing step of the development process. When new changes to the develop branch are made and are ready for testing, the develop branch is merged into this one. When new changes are pushed to this branch, it will go through a combination of unit tests, integration tests and other various forms of testing.
Stage: This branch represents a ‘mock production’ environment. While the test branch undergoes initial short-term testing, this branch is intended as the candidate for the production branch. This branch should be placed in a completely isolated, internal environment that acts as a staging area before it is merged into the production branch.
Production: This branch represents the most stable branch in the repository. The code on this branch is intended for deploying to production servers. It is intended for use by clients or the public. Only code that has been thoroughly tested should be pushed or merged into this branch.
One more quick thought: You should delete the master branch to avoid confusion as to which branch is the most stable.
Use this command:
git branch NAME
where NAME is the name of the branch to be created. To switch the local repository to that branch, use:
git checkout NAME
where NAME is the name of the branch to switch to.
Managing the Four Branches: Environment Workflow.
Now let’s focus on the workflow of the four-branch pipeline outlined above.
First, new features or bug fixes are pushed to the develop branch. It’s important to note that you should only push completed bug fixes or features to this branch. This is covered in a later section called Feature Branches below. When enough of these changes are made to warrant testing (which could be every new feature or a certain number of them), develop is merged into test.
When this happens, the code on the test branch will be run through a test suite — essentially a barrage of unit tests and integration tests. Other types of testing should be done on this branch as well; “click through” testing of the UI component (if the software contains one), for example, provides testers with a better understanding of the user experience and allows them to identify less obvious issues. If all tests succeed, the code should be merged to the stage branch.
New changes on test should not be pushed stage if any of the tests have failed. New changes that get merged into the stage branch should be deployed onto an isolated, internal environment that mirrors the production environment. Further testing should be done on this environment, including tests that are more long-term. This includes stability tests under heavy loads for long periods of time, and tests for optimization and efficiency. One potential outcome of running the application in a “production-like” environment is that it could function less optimally or run slower than expected. In this case, it may not be desirable to merge stage into production. When such testing is completed and the new changes are ready, stage is merged into production. These changes should be updated on all production servers.
Without each of the branches above, different aspects of the development process get more complicated. For example, let’s imagine that we only have a develop branch that new changes are merged into and a production branch that is deployed to servers in production. In this instance, it’s difficult to ensure that the develop branch has been tested appropriately before being pushed to the production branch if there are changes merged to it constantly. If we’re missing only the stage branch, we may discover that once test is merged into production and deployed to the production environment, the application may behave less optimally (unexpectedly slow, for example).
So far we’ve covered goals two and three. But to accomplish goal No. 1, we’ll need to address feature branches.
When developing a new feature I recommended that you do not work directly off of the develop branch. Pull any recent updates to develop and branch off of it, and work on that instead.
Consider the following commands:
git checkout develop
git pull origin develop
git branch PROJ-103
git checkout PROJ-103
These commands set the current local branch to develop, pull the recent updates to develop, create a new feature branch called PROJ-103, and set the current local branch to that. You then implement the new feature on PROJ-103. You can make commits and push the feature branch to the remote repository without worrying about causing conflicts for others on the develop branch. When the feature is complete, merge it into develop with these commands:
git checkout develop
git pull origin develop
git merge PROJ-103
The PROJ-103 feature is easily tracked across the development process. It is extremely important that a feature is broken down into as small a task as possible before creating a feature branch. Ensuring that each feature branch has the least amount of changes necessary to complete the task is key to reducing complexity of merge conflicts. It is much better to have many smaller feature branches merged into develop than a few feature branches that each have many changes. In the latter case, it’s way more likely that more than one developer will change the same lines of code (introducing a merge conflict). The more conflicts there are in a single merge, the more complex the fix will be. Committing smaller changes more often will help to prevent complex merging issues.
It Worked For Us
The above strategy has enabled our team to become more efficient, with the added bonus of reducing merging issues (making the development process much less stressful). Has this or a similar strategy worked for you? Please enter your reply below.