Using git to slowly release features

Git has become a must have when developing both simple and complex projects. It's definitely the way to go when it comes to maintaining code integrity between a team working on the same project, and it's also our very own ctrl+z tool.
I'd say that it's indeed mostly used to easily store, update and distribute code between teams and projects, but in this article I'm going to address how git can be used to successfully manage a project's development (staging) and deployment structure. Basically, how you and your team can carry on developing and testing awesome features, and pushing those changes safely into a production environment.

Why use separate development stages?

When working on a continuous project, adding additional features aka candy is what it's all about. Having just a development environment and a production environment means that you can only develop a block of code, and then deploy it to production. That can become troublesome when:

  • New features are deployed without being thoroughly tested
  • Multiple team members are working on separate features
  • Slowly releasing specific features, when they are meant to be released
  • Performing fixes in specific project stages

Solution? Staging branches to the rescue.

What are the staging branches?

Staging branches are separate development threads (rc's) that take
advantage of the inherit branching ability of git. What this means is that you can have many development stages (divided into separate branches) before the actual production stage.

alt

The image above represents a staging model composed of 5 separate branches. master is the furthest one from the production, and it should be the branch used for the development of new features. Every time a new code version (collection of new features or updates) is staged to initiate development, the master stage steps forward into the staging chain, becoming rc0. master remains the exact same as it was, available for further development on top of the already existing code. With every new development stage or code version, the code moves forward another step in the chain, adding new features always on master.

Outside the box, this alone allows the continuous adding of new features, while keeping the previous versions still available for testing and isolated bug fixing. Moving forward with the development process and still delaying the release to production is quite a helpful feature.
The staging branch numbers will vary according to the project's complexity, for example, one can have just a master -> rc -> production logic. Doesn't matter how many branches you choose to cycle your code through, the overall goal is to make sure that by the time the once master becomes production, the code has been declared full proof.

If any bug is found on any rc, git allows you to easily checkout that branch and fix the bug.

How to implement it

Ok, let's setup this logic structure on an existing git repository. If you don't have one, it's time!
If you are using a repository with an already existing project on it, first make sure everything is synced.

Create the branches

First, since the master branch is already created by default, create the staging branches, like so (repeat for as many staging branches as you want)

$ git checkout -b rc0
$ git push origin rc0

Then, the production branch

$ git checkout -b production
$ git push origin production

Make sure all the branches are there with

$ git branch

If all went according to plan, all the branches should be created, and the exact same code should be in all of them. That's a good base to work on. At this point, you can start developing your new features in master while leaving production alone.
How about when those features are implemented and you want to move it to the next stage?

Pushing the release upstream

After a development stage is finished, it's time to move the new features upstream the staging chain. Eventually they will reach the production stage.

To push it, all you need to do is merge your changes onto the next branch. In this example, releasing master onto rc0

$ git checkout rc0
$ git pull
$ git merge origin/master --no-commit --no-edit
$ git commit -m "Releasing master to rc0"
$ git push

The procedure is the exact same with all the branches, including when releasing to production. The only change in further stages consists in doing the exact same procedure to all the previous stages. So, when releasing rc3 to production, you will also need to release rc2 to rc3, rc1 to rc2, and so on.
This will keep all your "N" previous code versions available for testing, debugging and fixing before reaching production. Neat.
If this is indeed a "deploy to production" type of push, you might want to add a tag to "brand" it, for later reference. There isn't that much functionality associated to git Tags, however, these can be a very good way to keep track of code versions. More on git Tags here.

To add a tag to the production deploy, all you need to to is add this, after the push command

$ git tag -a v1.0.0    // for example

You can call your tag whatever you want, of corse. Using the classic versioning format helps though.

How to fix bugs

Bugs are elusive, tricky and utterly magic. These can and will happen in any stage and release. It's only logical. Being said this, the way we address this issue depends on where it happens. The first thing to do is find out in what stage did the bug emerged. Let's say it was originate in rc1.

Checkout to the branch

$ git checkout rc1
$ git pull

Create a hotfix branch

$ git branch -d hotfix-rc1

fix it

$ git add <the changed files>
$ git commit -m "Bug fix"
$ git push

Keep in mind that all the previous stages will most likely share that bug, since the code that spawned it is present in all of them. This means that once you fix the bug, you will have to apply the same fix to rc0 and master. You can do this with by merging the hotfix branch with the downstream stages. Think of it as branch wise cherry-picking.

$ git checkout rc0
$ git pull
$ git merge hotfix-rc1 --no-commit --no-edit
$ git commit -m "Downstream bug fix: rc0 <- rc1"
$ git push

Apply the same treatment to all the other downstream branches, which is only master, in this specific case.

Remember that if you are doing a hotfix to production, you might want to increment the tag version. Since there is nothing actually maintaining the integrity of the git tags, make sure you update to the proper version, like so

$ git checkout production
$ git pull
$ git merge hotfix-rc1 --no-commit --no-edit
$ git commit -m "Hotfixed production"
$ git push
$ git tag -a v1.0.1

Tip

If this upstream/downstream sounds too tricky, just remember that bugs are fixed downstream, and releases are pushed upstream.

Deleting the temporary hotfix branch

The branch for bug fixing is only meant to be temporary. After the fixes have been applied to all the necessary branches, delete the branch with

$ git branch -d hotfix-rc1

And you're done!

comments powered by Disqus