January 17, 2010 by thilo
When working with our clients we use pivotal tracker to manage requirements and priorities. Which works very well, as it keeps overhead low and offers a very focused communication channel for feature related questions.
The last step for a successful delivery of a feature requires our customer to accept that feature or hand it back to us if something isn’t satisfying. To enable our customers to do that we provide them with a staging environment, which is a non public version of our customers’ product with the latest code integrated so that he can try out new features before they go live. We deploy to the staging system directly from the master branch usually, from time to time we introduce feature branches if we think something takes longer or might need refinement after more customer feedback. By the time of an actual live deployment we normally have ironed out any remaining issues which shown up during the client’s review on the staging system.
This simple system works pretty well and effective if you are in control of the staging environment, have only one person as the customer to talk to and your developers’ communication works like in a hive mind (which it does, mostly :)). This system reached its limit when we worked with PaperC lately, one of our valued clients.
We coached their programmer in BDD and pair programming some time ago and still working with them very closely to get improvements and new features out of the door fast. The limits with our simple workflow became visible when I paired with Patrick together for PaperC and they also had a pair on site with us at co.up. At first delivered tickets began to stack up waiting for acceptance, also because the staging system became unstable as issues weren’t fixed fast enough.
Things got more complicated as we put features on staging which weren’t supposed to make it into the next live deployment as they where part of a bigger, not yet thought to an end, interface change. These features just should be shown in action to see how things can go on from what we did so far. This incident cost us some extra time to put these feature related commits out of the master branch into a feature branch. So we switched to create a feature branch for everything that was more than “add a new input field to that”. This left us with a lot of branches we had to keep track of when deploying to the staging system, but at least we could undo merge commits easily when a feature wasn’t suppose to be deployed live. Then we started a discussion on how we could do this in a better way. Before I describe the workflow which I think is best suited, as we already employed it successfully in the past, I’d like to mention two suggestions I’m not comfortable with an why.
One idea was the introduction of pair branches, where every pair commit their work to. And these branches would be merged into master for acceptance. Apart from not really resolving the issue, it just mitigates it onto a pair branch, and it contradicts the principle of collective code ownership.
Another proposal was to create a special production branch and selectively cherry-pick commits which are supposed to go live. This points into the right direction, as it gives you more control of what goes live, but it might leave you with a lot of commits to pick from, if you have a developer like me who commits rather often. And it doesn’t give you a single point to roll back from if a feature that has multiple commits get pulled off the release.
So now for my preferred and practically proven solution. First off, it requires an extra server which I refer to as experimental. It is a lightweight (not so close to production setup) staging system with a database schema that should be easily migrate-able up and down like on a CI server. It should be fed with some test data automatically. It doesn’t have to be as much data as on staging, just enough to try out most features.
Now to the git workflow part. Smaller changes and bug fixes go directly to master as always. Bigger features should be done in separate branches and merged to master once things are done (Don’t forget to delete the remotes if the feature gets accepted). The master branch should be deployed to the experimental server at least once a day, likely more often so that features can be quickly accepted. Features that aren’t necessarily ready for production yet can be deployed to experimental for acceptance testing simply by changing the branch from which the experimental environment should be deployed from the master branch to the feature branch and rebuild the database if required. Before an imminent release the master should be pulled to staging so that any remaining issues can be fixed. Such fixes should happen on the staging branch and can later be merged back to master. If everything is fine, merge staging to production and deploy to the live system if you are ready. This way you always have a clean production branch, it’s easier to pull off features from a release and get faster feedback for completed features without worrying to mess up the next production release.
What is your approach to managing commits and releases? Do you find this post helpful? Feel free to leave some feedback in the comments.