In this post, we will go over why branching is required, the difference between development, staging and production environments, why a strategy is required for branching, and look at a good Git branching strategy.
It is a general practice to commit your changes very ofter regularly, so that the changes are not missed.
There is a saying among developers that start the day by pull, take a break on commit and end the day by push
Why branches are required?
Let us take that we are building a react app and one developer is working on the header component and another developer is working on the footer component. It may even be you, a single developer, who first wishes to work on the header, then perform a commit, work on the footer - perform a commit, again want to work on the header, and so on...
Now let us say that we don't want the latest footer. We can say that when showing a demo of the features to the client, the client says he does not want a footer.
Now we don't want changes in commit 6 and want to revert to commit 4, it becomes harder to track as the changes in commit 5 exist if it is in a different file which is not accounted in commit 4.
It becomes so confusing to bring back the previous state. It becomes very hard to track the commits. And reverting a commit is very common during any feature development.
The features x(header) and y(footer) are coupled together in the master branch, and a change in feature y causes an error, the same error will affect the development of x.
Let us say, we create a branch for the header component and footer component respectively as shown in the above image. Now, if the footer component is throwing an error, it does not affect the header as the header component is in a different branch.
Once the development is complete, we can merge the branches back into master.
Now, if the client says he does not want the footer, we can just revert to commit 7. If it is before the merging, we will just abandon the footer feature.
Now that we understand the need for branches, if we keep creating branches as we want, it will lead to an even harder time managing the features. If Developer 1 follows a method, developer 2 follows another method, and so on, it causes a pandemonium. Hence we need to devise a strategy, which will be followed by all developers in the team.
Before jumping into the branching strategy, let us have a look at what is environment and the different types of environments.
Environment and the difference between development, staging and production environments
An environment in this context, includes the necessary hardware(single or multiple servers), Software dependencies, database, compiler/interpreter, network access for an application to run, and the application is hosted and tested in such an environment. There are many environments like training, PSR, and so on. Among the major environments, the main 3 environments that should definitely be used are development, staging, and production environments.
This is the environment where you develop your code and test it. All the commits made you or your fellow developers will be built and tested in this environment. It might be your local machine. But the best practice is to have it deployed in the cloud or your local datacenter, like a production would be hosted. By having the environment similar to prod, we will be able to find out issues at the early stages of development.
It will be connected to a dummy database. The changes you make in the development environment will not be visible to the users when they view your application. This is just for you and the other developers to see how new features will work and to try out improvements.
Various unit, integration testing will be carried out based the testing model in this environment. A thorough check of the code is done at this phase to reduce the bugs.
After the testing and the feature is agreed for a release, the code is moved to the staging environment.
The staging environment is as similar to production as possible.
The idea is that if a feature is running perfectly fine in staging, it will run in production also.
Generally, the acceptance tests are carried out in this environment, but depending on the testing model, it can be carried out in the development environment also.
Smoke tests and most of the performance, reliability, security and availability testings will be carried out in this environment.
All pre customer releases will be made in this environment. The customers/clients will be shown a demo in this environment before releasing it to production and making the application live.
This is the environment where the application is live and being used by the customers. Any bug found at this stage will be by the customer. Hence we try to fix the issues as much as possible in dev and staging environments. If any bug found, we just have to hope that it's not going to break the application.
In the production environment, the application will be hosted in a highly available infrastructure setup with minimum downtime. Sometimes, the releases are done in partial rollouts, where it is released to a subset of the customers and after finding that there are no bugs, releasing the features to all the customers.
The environments can be called by other names like dev, test(instead of staging), release(instead of production). Setting up three environments can be a pain but it is worth the effort, to test everything in dev and staging than facing the issues in production.
There are many different branching strategies available. We will look at one such strategy which will immensely aid the release management.
Initially, the default branch would-be master. We will create the staging and develop branches and we will make develop branch as the default branch.
- develop (default)
The above branches should be created before developing any features and should not be deleted at any point in time, as long as the project/application exists.
The analogy is that any commit in develop branch will be deployed in the development environment, any commit in the staging branch will be deployed in the staging environment and any commit in the master branch will be deployed in the production environment.
So for any new feature to be developed, a new branch should be created from develop branch, which should be named like feature/x.
For any bug to be fixed, here also a new branch should be created from develop branch, which should be named like bugfix/x
huge project with many users
In a huge project involving many users, a single feature, depending on the need, can be worked by many users. In that scenario, from the feature branche, we can create a user branch, which should be named like userX/z
Once the task of the user is completed and tested in user's branch, it will be merged to the feature branch from which it will be created using a pull request.
Once a feature is developed and tested or a bug is fixed, the feature or bugfix branch will be merged to develop branch using a pull request.
Assuming there is a feature for header, a bugfix to fix the title of the page, and the header feature has two tasks for header width and header color and is developed by two developer user1 and user2, then after creating branches in the above pattern, it would be like below:
- develop (default)
I'll go through some of the rules which are generally followed for the branches.
- This is the main branch, whose code is deployed in the production environment and being used by the customer in live.
- No new branch can be created from master.
- No commits can be directly made in master branch.
- It should accept pull requests only from the staging branch and it should not accept pull requests from other branches including the develop branch.
- Should not have unreleased code [ every commit in master must have a stable release associated ]
- The pull request from the staging branch should be reviewed and approved by the client/stakeholder/productOwner.
- The code in this branch will be deployed in staging environment
- No direct commits are allowed.
- It should accept pull request only from the develop branch and it should not accept pull requests from other feature or bugfix branches.
- It can make a pull request only to the master branch.
- No new branch can be created from staging. In case of any issue is found while testing or deploying the code in staging branch, devfix/bugfix branch should be created only from the develop branch.
- Should not have unreleased code [ every commit in staging must have a beta release associated ]
- The pull request from the develop branch should be reviewed and approved by the tech lead/scrum master.
- The code in this branch will be deployed in development environment.
- No direct commits are allowed.
- It should accept pull requests only from the feature or bugfix branches and should not accept pull requests from user branches under any circumstance.
- It can make a pull request only to the staging branch.
- Should not have unreleased code [ every commit in develop must have an alpha release associated ]
- It is the base branch for feature or bugfix branches.
- Any new feature should have its own branch created from develop branch.
- The naming convention to follow for the feature branch is feature/$(x) where x denotes the name of the feature to be developed.
- Direct commits are allowed only when a single developer is working on the feature.
- In most of the projects, we will not have user branches. A feature will be owned by one developer.
- If multiple developers are working on the feature, pull requests need to be created from the user branches and merged.
- It can have unreleased code.
- Any new bugfix should have its own branch created from develop branch.
- The naming convention to follow for the feature branch is bugfix/$(y) where y denotes the name of the bug to be fixed.
- Sometimes, when the bug is being fixed under multiple scenarios, we can use numbering or keyword denoting the scenarios to identify the bug. Hence the same bug can have multiple branches.
- Direct commits are allowed.
- It can have unreleased code.
- We can delete the branch after fixing the bug.
- For each user working on a feature, a new user branch is created from the respective feature branch.
- The naming convention to follow for the user branch is $(x)/$(y) where x denotes the name of the user and y denotes the name of the sub-feature to be developed.
- Direct commits are allowed.
- Only a single user is allowed to commit in this branch.
- It should not have a release.
We have been using this strategy in my team and it helps a lot in code and release management. This strategy is also very helpful in setting up a CI / CD pipeline.
Top comments (6)
This strategy has been proven to be an anti-pattern though, for more on this: martinfowler.com/articles/branchin...
why would you branch new features from develop? that means you are bringing in untested code from others that may not even make it to production.
How do you handle hotfixes into master/production if the develop/staging branch has changes (maybe new features) that cannot be pulled into production yet?
Though not a frequent occurence and should be avoided, but in those specific scenarios, we can create a new branch from master, test the hotfix and create a pull request to master which should be validated and approved by people other than the dev who makes the change.
Do suggest a methodology incase you have something in mind
You don't explain why the dev is making multiple commits for a feature (e.g. footer).
Branching is powerful but not the only paradigm.
I agree with you that branching is not the only paradigm. I am just explaining about the branching strategies and good practices to follow and there's a saying that start the day with a pull and end the day with a push to prevent any loss of code. So as you say, footer is just an example and consider a big feature !