Adapting version-control branching workflows to aid productivity

If you're using git (or indeed any other version control system, DVCS or not) then you're probably striving to: work safely (ensuring your work doesn't complicate other people's); while still getting things done (ensuring your work has a clear path to going live).

A reasonable simplification of how you might accomplish this is by following steps a bit like this.

  1. Identify a circumscribed body of work and agree on its criteria for done
  2. Pull any recent changes from other developers into master
  3. Check out a new branch
  4. Do the body of work, pushing as you go if the work is substantial
  5. Get your QA team/client/yourself to check the work is done as per the criteria
  6. Merge the branch into master and push master

Six items sounds complicated, but only the 3½ items in bold are really anything to do with git (which isn't to say the non-git steps are any less important to a robust workflow.) More succinctly, the fundamental, siimplest git workflow can be represented as:

pull; branch; work; merge; push

... with extra steps added in as you see fit e.g. pushing intermediate work to a remote repository for backup purposes.

However, if you work in a larger team, you're likely going to want to formalize this a bit more. But what aspects should you (or indeed can you) formalize, and to what extent?

Adopting conventions

The first big step you can take is to adopt conventions within your team, and document them: to your next co-worker, freelancer, consultant or even your boss, an undocumented convention is a convention that does not actually exist.

There are I'm sure plenty of naming conventions you could adopt, but two that I really like are:

  1. Drupal patch naming conventions for branches. Whatever you think about Drupal (like Linux) still using a patch-based workflow, these conventions are well thought out, and provide a good context of categorization (which project does this belong to), uniqueness (the issue ID and comment-in-thread ID) and a brief human description.
  2. Semantic versioning for tags. If you've ever puzzled over exactly which bit of your version numbering you should increment in the next live tag, then this is for you. Again, a simple, unambiguous off-the-shelf solution, which you might need to adapt for web projects (what's an incompatible API change?) but which can nonetheless stand you in good stead.

As implied in the introduction, branches and their names should march in step with work units, as closely as possible; to keep that relationship unambiguous, every unit of work that needs separate signoff, will need its own ID (usually represented in a ticketing system). If you've broken down an initial "epic" into multiple units of separable work, then each new unit should have its own, separate ID too. There ought to be no exceptions to this: if your work tracking system does not provide you with unique IDs within an epic, then at least work out your own alphanumeric ones and attach them to the tickets e.g. 575-auth, or 1276-parsingjson. Or use a better system.

Along with a unique ID, branch names should also have a prefix which determines the type of work being done. With a nod to the next step, a reasonable prefixing scheme is:

  • hotfix-  urgent fixes which will need to be deployed rapidly to the live site e.g. fixing serious bugs
  • feature-  new features e.g. important/substantial but not urgent change/addition requests to the project's functionality 
  • merge-  when you need to merge two features for QA, prior to approval and hence merging into master.

But you can certainly adopt your own convention: the key thing is to document it; and when ambiguities are discovered, discussed and resolved, to update the documentation accordingly. And so, with all of the above in mind—type prefixes, unique IDs, and Drupal's patch naming scheme—here's a suggested schema for your branch names:

[type prefix]-[unique ID]-[short, human-readable description]

So, for example: "feature-998-upgrading-webform"; "merge-755-767-new-workflow-with-subscriber-users"; "hotfix-54-whoops-barry-broke-single-signon". Actually, I leave it up to you (and the tribunal) as to whether you actually blame members of your dev team within your branch naming scheme.

Putting the unique ID before the description—a departure from Drupal's convention—aids quick tab completion e.g. with zsh's git module: you can type "git checkout feature-123-" and press tab, and zsh will complete the branch name for you, without you having to remember the textual description.

The exact schema is of course up to you: as long as you document it!

Abstracted workflow adaptable for most projects

Now you have something which is standardized for in-house use, what's the next step? Well, most companies can stop there, especially if they hire rarely, or don't use freelancers or other outside talent. However, what if you're wanting to bring outsiders into your project on a fairly regular basis? You certainly have documentation, but is your team's time best spent writing and updating good documentation—a skill in itself—or coding? 

As an alternative to homegrown workflows, git flow provides an abstraction of very many likely project workflows, into something standardized that most of those existing workflows can be fitted to, with varying amounts of change. But I would argue the amount of change required is frequently very small, especially if you're already taking care to always branch, and you already have naming conventions in place.

For example, the team I used to led at Torchbox moved to git flow very straightforwardly in part because it was so close to what we were already doing. Afterwards, upskilling incoming talent was much less fraught with ambiguities, because several paragraphs of in-house documentation could be replaced by the three words: "use git flow.html". If the developer hadn't used git flow before, then the already available documentation for it was excellent; if they had, then there was almost nothing else we needed to tell them about it.

And if git flow is too heavyweight for you? It's worth remembering that "pure git flow or nothing", like "pure agile or nothing", is a false dichotomy. Git flow isn't an edict handed down from a deity, and if your team wants to, say, drop the use of the develop branch, or not bother with separate release- branches, then as long as that works for you, nobody is going to take your git flow ISO certification away, because there isn't one.

Easing off on the complexity?

In this spirit, Github's own development team have adopted a somewhat less complex workflow, which they call "Github Flow". As the name suggests, though, part of git flow's complexity is pushed into Github: admittedly making use of the existing work that they've put into their own dogfood, but also tying the workflow to use of the site.

Another big simplicity that Github Flow makes is only possible because the team operates 100% continuous deployment: when something's ready to go live, it "can and should deploy immediately". If your workflow however needs to involve someone that isn't online at that point, or signoff by someone who needs a staging site set up, or even a telephone call to walk them through it: then maybe you need to drop some of that complexity back in.

There is no perfect workflow, but there are many good ones

Ultimately, whatever workflow you adopt, it needs to be as good as possible for you, your team, your project, your client and indeed a future consisting of changing circumstances: with all those competing factors in play, no one workflow is likely to cover every eventuality:

  • Homegrown workflows can be perfect for your existing team, today; but they invariably have a learning curve, as your team must introduce outsiders to it.
  • Git flow can be a perfect abstraction, in that you can apply it to almost any project; but the complexity that abstraction entails can slow your team down.
  • Github Flow is perfect for the Github team, who use Github for their QA process and deploy continuously; but it can feel too stripped down for some other teams.

What's important is to find a workflow that works well for you, your team, your project etc; be happy to adapt that workflow, as long as you document departures from any standard; and regularly revisit your workflow, critiquing and proposing modifications, and keeping your processes truly agile.


Edit: I should add that Matthew Owen, Robbie Mackay and Tom Dyson all provided feedback that helped me put this together (thread here): thanks, all of you!

Nice write up.

I think the important bit when figuring out git workflows is to get something that fits. First figure out 1. are you trying to get a git workflow that fits your current process? or does your current development process have issues you need to fix too?

Git is intentionally flexible. Forcing yourself to follow git-flow when you don't really have 'releases' doesn't make sense. Tweak your branches to match bits of your workflow that already exist:

  • We branch for features and bugs .. because we already treat those as separate work (tasks in a tracker)
  • Merges really represent code reviews. The important bit is the review, the merge is just an implementation detail.

If you don't do code review.. maybe adding branches is a chance to add some review process?

There is no perfect workflow, largely because you have to make it up for each team.