Skip to main content

Branching and Staging

Intro

Branching strategies

There are many different branching strategies available. From ODJ we want to use the simplest flows to reduce the overhead on branching. We recommend to use trunk-based development.

An overview about different branching strategies can be found here.

Staging

The challenge is to get an useful and obvious mapping from your source code branches to your runtime stages. For ODJ, there is a productive branch, which is treated special in the pipeline. Whenever you push to this branch, this is treated as a wanted deployment to production. For all other stages you must decide when they should be deployed.

Naming

It is hard to find a correct naming for the different ways of mapping. We tried to use the git branching strategies to name it. We don't want to focus on "how" you use branches and pull-requests in this overview. The focus lay's on the mapping to stages.

Trunk-based development

Recommended

This is the recommended branching strategy from ODJ

Trunk-based development is a branching strategy that in fact requires no branches but instead, developers integrate their changes into a shared trunk at least once a day. This shared trunk should be ready for release anytime.

The main idea behind this strategy is that developers make smaller changes more frequently and thus the goal is to limit long-lasting branches and avoid merge conflicts as all developers work on the same branch. In other words, developers commit directly into the trunk without the use of branches.

Consequently, trunk-based development is a key enabler of continuous integration (CI) and continuous delivery (CD) since changes are done more frequently to the trunk, often multiple times a day (CI) which allows features to be released much faster (CD).

This strategy is often combined with feature flags. As the trunk is always kept ready for release, feature flags help decouple deployment from release so any changes that are not ready can be wrapped in a feature flag and kept hidden while features that are complete can be released to end-users without delay. 

This is a supported strategy in ODJ. All default branch policies are applied automatically to the productive branch. It is for a experienced teams which do a lot of pair programming or extreme programming.

info

Pull-requests are not required in trunk-based development. If you don't use them, you have to disable the automatically applied branch policies from the ODJ.

Further documentation

Staging example: Trunk-based fast forward deployment

This is an example for an easy and simple deployment from your productive branch to your prod stage.

image

Usecase:

  • Should be used by experienced teams together with pair/extreme programming
  • You should have feature toggles implemented

Specials:

  • Short-living feature branches (without pull-request)
  • Only productive stage
  • You should have feature toggles

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
odj_deploy_stages:
- prod
...

Staging example: Trunk-based development with qa stage

This is an example for an easy and simple deployment from your productive branch to your stages. It creates a complete build of a Docker container which is then deployed to all defined stages.

image

Usecase:

  • Should be used by experienced teams together with pair/extreme programming
  • You should have feature toggles implemented

Specials:

  • Short-living feature branches (without pull-request)
  • Deploy one deployable to QA stages with optional approvals for prod stage

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
odj_deploy_stages:
- qa
- prod
...

GitHub Flow

Supported

This is a supported branching strategy from ODJ.

GitHub Flow is a simpler alternative to GitFlow ideal for smaller teams as they don’t need to manage multiple versions.

Unlike GitFlow, this model doesn’t have release branches. You start off with the main branch then developers create branches, feature branches that stem directly from the master, to isolate their work which are then merged back into main. The feature branch is then deleted.

The main idea behind this model is keeping the master code in a constant deployable state and hence can support continuous integration and continuous delivery processes.

This is the recommended strategy in ODJ. You have to define one branch as your productive branch on which the default branch policies will be applied to.

Further documentation:

Staging example: GitHub Flow

This is an example for using a simple GitHub approach with mapping of feature branches to a stage. The productive branch will be deployed to the stages qa and prod.

image

Usecase:

  • For teams with a simple development and release process
  • Should be used by teams with mixed experience levels
  • Optional: Deploy to a QA stage which does automated QA testing to ensure quality before deploying to production

Restrictions:

  • none

Specials:

  • Long-living feature branches with merging to productive branch via pull-request
  • Feature branches can be deployed to stages

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: # deployment from productive branch
odj_deploy_stages:
- test
- qa
- prod
${{ elseif eq(variables['Build.Reason'], 'Manual') }}: # manual deployment of a branch
odj_deploy_stages:
- dev
   post_deploy_map: # optional: example for QA tests
qa:
- script: echo "This can be your automated QA testing task. If tests fail, there is no deployment to production"
displayName: 'QA testing task'
...

Staging example: GitHub Flow with hotfix

This is an example for using a simple GitHub approach with mapping of feature branches to a stage. The productive branch will be deployed to the stages qa and prod.

The hotfix branch could be used optionally to deploy to production in urgent cases. The hotfix branch must be merged ASAP to the productive branch.

image

Usecase:

  • For teams with a simple development and release process
  • Should be used by teams with mixed experience levels
  • Optional: Deploy to a QA stage which does automated QA testing to ensure quality before deploying to production

Restrictions:

  • None

Specials:

  • Long-living feature branches with merging to productive branch via pull-request
  • Feature branches can be deployed to stages
  • ⚠️ Hotfix releases must be merged ASAP to the productive branch

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: # deployment from productive branch
odj_deploy_stages:
- test
- qa
- prod
${{ elseif contains(variables['Build.SourceBranch'], 'hotfix') }}: # hotfix deployment
odj_deploy_stages:
       - test
- qa
- prod    
${{ elseif eq(variables['Build.Reason'], 'Manual') }}: # manual deployment of a branch
odj_deploy_stages:
- dev
   post_deploy_map: # optional: example for QA tests
     qa:
- script: echo "This can be your automated QA testing task. If tests fail, there is no deployment to production"
displayName: 'QA testing task'
...

Staging example: GitHub Flow with tag deployment

This is an Example of a GitFlow with deployments controlled by GIT tags. Every time a tag is set, a deployment to that stage is triggered.

PROD stage

There is one exception for the productive stage. This must be a GIT branch (not a tag!) as some further added tools don't work well with tags (e.g. SonarQube).

Pipeline trigger for tags

By default, Azure DevOps pipelines will not start when setting a tag. This behavior changes, when you define the "tags" parameter in the "trigger".

image

Usecase:

  • For teams with a simple development and release process
  • Should be used by teams with mixed experience levels
  • Optional: Deploy to a QA stage which does automated QA testing to ensure quality before deploying to production
  • Using tags as trigger to deploy to defined non-prod stages

Restrictions:

  • ⚠️ Productive deployments must be done via the productive branch, don't use tags for it

Specials:

  • May use long-living branches with pull-requests

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'    
${{ if and(startsWith(variables['Build.SourceBranch'], 'refs/tags/'), endsWith(variables['Build.SourceBranch'], '/dev')) }}: # Example of tag with sub-folders
odj_deploy_stages:
- dev
${{ if eq(variables['Build.SourceBranch'], 'refs/tags/test') }}:
odj_deploy_stages:
- test
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
odj_deploy_stages:
- qa
- prod
...

GitFlow

Not recommended

This branching strategy is not recommended to be use it in the ODJ. It's complex to integrate it to the default pipelines

Considered to be complicated and advanced for many of today’s projects, GitFlow enables parallel development where developers can work separately from the master branch on features where a feature branch is created from the master branch.

Afterwards, when changes are complete, the developer merges these changes back to the master branch for release.

danger

GitFlow was used heavily in the past, nowadays it is treated as "heavy" and "outdated". 

This is a partially supported strategy (with restrictions) in ODJ but not recommended to use it. You have to define one branch as your productive branch on which the default branch policies will be applied to. You have to create your feature branches by hand.

Further documentation:

Staging example

This is an example of a more complex GitFlow with release and bugfix branches.

danger

We recommend to use GitHub flow with tags and/or automated tagging from pipeline templates after deployment

image

Usecase:

  • Long running QA tests and business approval processes
  • You don't have feature toggles implemented

Restrictions:

  • ⚠️ All deployments to productions must be made through the productive branch, even the bugfixes/hotfixes. All changes must be merged to the productive branch first.
  • ⚠️ The productive branch must be set to a "static/fixed" branch (e.g. required for SonarQube). The "release-*" branches are not usable for that.

Specials:

  • This strategy is only partially supported, as deployments to production cannot be made from all branches

Example pipeline configuration:

...
extends:
template: /templates/technologies/maven/v2/odj-pipeline-template-technology-maven.yml
parameters:
productive_branch: 'refs/heads/main'
${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}:
odj_deploy_stages:
- test
- qa
- prod # optional approvals before deploying to this stage can be configured in Azure DevOps
odj_deploy_stages:
- dev
...