Git Workflow / Branching
This article describes how code will be structured in branches and how a normal workflow can be set in order to allow developers to write, test and deploy their code safely and independently.
We are going to describe 2 schemes:
- Continuous Versioning, where clients cannot be connected to a previous version and they are always automatically updated to the latest version
- Multiple Versions, where clients can use previous versions since the project is distributed and there is no control over the clients.
Scheme 1. Continuous Versioning
This is the scheme where the product follows a linear path with versions. In other words, all the users will use the new version once it’s out. This scheme usually applies to services that are centralized, like a website or a web application.
There are 2 standard branches in this scheme, master and develop.
Master. The master branch is the one that corresponds to the production environment and everything that is on master is currently or will be soon to production (after testings etc.).
Develop. The develop branch is the one that developers use to create the feature branches and merge them back to master once they are ready. It can be used for QA environments, based on the needs of each project and their developers.
For each derivative branch from the above, we can use the symbol / to separate the category from the branch name.
For example: feature/new-exciting-feature, hotfix/bug-xyz
The naming reveals also the reason for creating the branch.
A simple example/guideline could be the following:
- New feature → feature/
- Improvement / Fix (not crashing bug) → fix/
- Crashing bug → bug/
- Hotfix (small fix to production) → hotfix/
We can assume that the two basic branches have some common characteristics.
Master branch is:
- Stable at any time
- All changes (both hotfixes and normal urgent-to-be-deployed features) can be merged directly to master
- This branch will be able to be deployed to a staging environment for extended testing
Develop branch is:
- Stable at any time
- All new features can be merged directly to develop, after being tested separately
- Everything that goes on this branch will have to work properly (+backwards compatible)
- Developers and testers will know what’s on qa environment at any moment (reduce questions like “is this feature on qa?”)
Based on the branches described above, we are going to separate each case and present the branches required and the workflow.
At any moment in time, a screenshot of our repository workflow may look like this. Below we will analyze which actions are valid in our workflow and how they can affect our repository.
For each new feature, a developer will create a branch from the develop branch like feature/new-feature-branch. When the feature is done, the PR will be created towards the develop branch.
This way, both developers and testers will know when this feature is on QA (develop branch) without the intervention of a third person.
This is the case where a feature (or improvement) is needed immediately and we want to deliver it as fast as possible. In this case, the branch will come from the master like feature/improvement-branch. Due to the fact that this feature comes from master, it makes little sense to test it on QA, but it can be tested in a production-similar environment, like pre-production.
No specific label is needed for this PR, the feature will be delivered on the next deployment.
The branching will be exactly the same as above. The only difference is that the PRs will be annotated with a hotfix label which will indicate that this PR is more urgent than the others.
Hotfix PRs can cause the deployment to run earlier than scheduled in order to fix broken stuff.
- On qa, the develop branch will trigger automatic deployment (on every push) to the qa environment.
- On production, we can have the option to deploy on push or manually. It shouldn’t make any difference, only if we need to run special tests before deploying to production.
- Optionally, one can have a staging environment where all tests will run before deploying to production.
This continuous delivery will allow developers to know where their code is, to know when the feature is on qa. It will also allow testers to know exactly when a feature is available for testing, without external reports sent.
Scheme 2. Multiple Versions
This is the scheme where the product can be released in different versions and different clients might use different versions of the product. This scheme can apply to APIs, code integrations to the client side.
This Scheme is similar to the Git Flow Model proposed by Vincent Driessen, with the difference that the release branches are permanent, as long as the version is live and under maintenance, and come from the master branch.
The branches are similar to Scheme 1.
We start explaining the workflow having in mind that the only branches in the repository are master and develop (and maybe features).
As described in the introduction, this Scheme is used when different versions of the code can be used by different clients at the same time.
The above model is just an example of how the versions can be separated in different branches. If one follows the semantic versioning model, we could suggest to create different branches for different major version. The logic behind this is that minor versions don’t break backwards compatibility and thus they don’t have to be maintained in different branches, whilst major versions break backwards compatibility and should have different branches at all.
The workflow is similar to Scheme 1, with the following additions and differences:
Create new version
Once the master branch has all the necessary features needed for the product to be released to the clients, we create a new branch from master and we name it based on the version (feel free to use any name indicating the version like 1.0 or v1.0 or version-1.0).
We have to take into consideration that from now on new features will be included in the next version.
We can use conventions to decide when to create a new version branch. Usually we can create new branches on every minor version update (from 1.0 to 1.1), but we can deliver improvements in the same branch (from 1.0.0 to 1.0.1).
A new feature will be developed on the develop branch like in Scheme 1. Once the feature is ready, it will be merged to master. The feature will be delivered to the client once a new version is created.
An improvement or a bug, once it’s recorded or documented, it should contain the affected version(s). Thus, the new branch will be created from the minimum version affected.
When the improvement or bug is finished, the branch will be merged in the version branch and a new patch will be released (from v1.0.0 to v1.0.1).
If the improvement/bug affects more than one versions, the feature can be merged from one version branch to the others.
We saw how we can structure our branch model to support multiple versions but when it comes to delivery, there is a different challenge. You can read more about the continuous delivery of different versions of our project here.
Thanks for reading! Feel free to comment and discuss about the above approach. If you like the article please “hit” the green heart to recommend it!