Git FTFY: branching model + CI [continuation of "Git Flow considered harmful"]

Hello dear reader, "Git Flow" is broken.

I'm pretty sure a lot of developers feel that internally, but not many of you did actually change something about it, right?

I was same too for ~5 years, then I read Git Flow considered harful and started moving in that direction ~1 year ago, finally dropped Git Flow completely this month and very happy about it.


1) No master branch

Because it's broken concept and makes no sense, especially for a project that may be in a state of parallel development of several versions of the product.

We have this instead (example data):

  • dev/1.1.0 (set as default branch)
  • dev/1.2.0 (while dev/1.1.0 is alive we periodically merge it into higher version branches)

When 1.1.0 is released -> set dev/1.2.0 as default, merge dev/1.1.0 to dev/1.2.0 and drop dev/1.1.0.

2) No release/vX branches

Because using word release in the branch name is broken by design and only confuses everybody.

Release is a final state, it can't be a branch, Git has tags for such state.

3) Tag each RC and release

When you think you're ready to give some version to QAs -> mark particular commit with tag as release candidate.

Switched to branch 'dev/1.1.0'  
Your branch is up-to-date with 'origin/dev/1.1.0'.

$ git tag v1.1.0-rc1

Then we use next algorithm:

1) If this build passes QA and we decide to ship it -> put a release tag on same commit $ git tag v1.1.0
2) If this build doesn't pass QA and we need to fix something -> do fix on dev/1.1.0 branch and tag next rc $ git tag v1.1.0-rc2 then go to step 1).

No more confusion of which state of the branch is the release one, no more need in tagging commits without really caring about those tags which lead to forgotten/missing tags. In this model tag is essential to build rc/release.

4) Hotfix/Patch: branch + tag for RC/release

Same as regular version development. Imagine we have a bug in already shipped v1.1.0.

$ git checkout -b dev/1.1.1 v1.1.0
$ # Do the fix, commit(s).
$ git tag v1.1.1-rc1 # Exactly same strategy as described above.

As you can see here we create a branch from the tag v1.1.0 of shipped version, tags matter.

K, that's it about branching. Every Git concept used as it should be. Consistency achieved.

Now about CI with branching/release model described above ^


0) Job to build Pull Requests

Obviously, this is just a must have. Ideally, it should rebuild PR with merge to target branch each time target branch gets updated.

Keeps last $n artifacts because we have a lot of such builds.

1) Job to build dev/* branches

If your 0 job rebuilds PR each time target branch changes with merge on new state of the target branch you may actually decide to not build dev/* branches separately.

But our QAs like this build job because they can easily pick latest build of some app version from this job.

Keeps last $n artifacts because we have a lot of such builds.

2) Job to build release candidates

This job builds each tag that contains rc and then notifies our QAs about the result. You can also deploy the result in some environment if it's ok for your project.

Keeps artifacts forever.

3) Job to build releases

This job builds each tag that does not contain rc and then deploys result or notifies the person who should deploy it.

Keeps artifacts forever.

CI Setup


Consistent and obvious branching strategy, tags make sense, no need in keeping old outdated branches. QAs and Products can finally understand CI and use it.

comments powered by Disqus