AgileFlow — The Agilest Git Workflow

Make your Git workflow an asset.

Michael Naquin
4 min readMar 19, 2024

TL;DR

- main
|
+-- feature/branch-1
| |
| +-- commit A
| |
| +-- commit B
| |
|<--+ # pull request/rebase
|
+-- tag: vYYYY.MM.DD # tag and deploy
|
+-- feature/branch-2
| |
| +-- commit A
| |
|<--+ # pull request/rebase
|
| ...
|
+-- tag: vYYYY.MM.DD # tag and deploy (from main)
| |
| +-- patch/YYYY.MM.DD # branch from versioned tag
| | | # (but only when there are fixes)
| | +-- commit A
| | |
|<--|---+ # pull request/rebase (to main)
| | |
| |<--+ # pull request/rebase (to patch branch)
| | |
| |---+ tag: vYYYY.MM.DD.01 # tag and deploy (from patch branch)
| |
|<--+ # pull request/rebase (to main)
|
| ...

Background

This is a batteries-included extension of the Git Feature Branch workflow, designed to clarify the process for releases and patches. Here we optimize for speed of development with just enough structure to fix critical production bugs.

While GitFlow is a very robust and cautious workflow, we want something simpler.

For teams that are on short sprint cycles — optimally one week — keeping this tight workflow will greatly reduce your overhead.

⚠️ Sprint cycles of 3+ weeks have either organizational complexities or perhaps too much technical debt where other Git workflows are more appropriate.

There Is Only `main`

By keeping our main development branch to exactly one, main, we focus both our bug fixes and feature fixes to one place.

There is no develop and/or staging branch to get recklessly ahead with unstable features. Or confusion about what feature is where. Or cherry picking of features to go in one of those or the other.

There is no giant merging of the branches to delay your release. No accidental leaving out of certain features or fixes. No need for team leads to make this the only thing they do.

There is only main. It is the river that carries all features and fixes.

Features

- main
|
+-- feature/branch-1
| |
| +-- commit A
| |
| +-- commit B
| |
|<--+ # pull request/rebase
|
+-- tag: vYYYY.MM.DD # tag and deploy
|
+-- feature/branch-2
| |
| +-- commit A
| |
|<--+ # pull request/rebase
|
| ...

Patches

You’ve checked everything for your deploy and it is perfect. And then comes along that one user that somehow manages to break the one thing. It is inevitable.

Let’s get that sucker fixed ASAP by making a patch branch off of our release tag.

- main
|
+-- feature/branch-x
| |
|<--+ # pull request/rebase
|
+-- tag: vYYYY.MM.DD # tag and deploy (from main)
| |
| +-- patch/vYYYY.MM.DD # branch from versioned tag
| | |
| | |+-- patch/branch-1
| | | |
| | | |+-- commit A
| | | |
|<--|---|---+ # pull request/rebase (to main)
| | | |
| |<--+---+ # pull request/rebase (to patch branch)
| |
| |---+ tag: vYYYY.MM.DD.01 # tag and deploy (from patch branch)
|
| ...

You’ll notice that we do a pull request to main first. Since most likely you’ll have a subset of developers working on the patch, everyone else will be working on main and more likely to encounter or be blocked by the issue.

This also sets the tone of keeping the main branch healthy and stable and keeps us from having to do any patch branch merging.

Calendar Versioning

Time marches on, and so do agile releases.

Very often libraries and open source projects use the venerable semantic versioning pattern. However, for internal projects that more or less move together, calendar versioning provides natural structure and clarity.

No more confusion of which version repo-a was used from repo-b.

Some examples:

  • vYYYY.MM.DD[.xx] - For very agile weekly/monthly releases. .xx is optional for patches.
  • vYYYY.MINOR.MICRO - For projects that are are more quarterly in nature. Like PyCharm or Unity.

️️⚠️ For projects that release to a schema like an API, especially externally, it makes more sense to stick to semantic versioning.

Linear History

If you really must, git merge away. However, defaulting to git rebase will offer you a clean linear history in the main branch. Since AgileFlow emphasizes the main branch this works especially well as it is the only destination of our feature branches.

Optionally we can rebase and squash our feature branches.

To enforce linear history, we can disable merge commits in our GitHub repo:

--

--