A flexible git workflow for teams.
We use a simple development model for our projects that’s lightweight enough to stay out of the way and flexible enough to handle real world usage.
Git is distributed source control
Git is the version control system of our choice. Each developer has a copy of the complete history of the project on his machine which reduces the likelihood of loss and its other features support the workflow we’ve designed.
Github provides great tools for code reviews through something they call “pull requests”. Github has the largest code collaboration network in the world and that means their tools have been refined through real usage. We’ve tried alternatives, but github is second to none.
One true master
With a few exceptions, our projects have a single branch of code from which the entire team will work. We don’t bother with maintaining different branches for different purposes because in practice (even in apps with > 20k monthly active users) you just don’t need different branches.
Instead, the whole team will remain focused on keeping the master branch stable for the rest of the team. With only one branch to maintain, it’s easy to treat it with the respect it deserves.
All of our work is built with automated testing, so that means all of the tests in the master branch must pass at all times.
Small Feature branches
Every feature, no matter how tiny, is developed in a branch off of master dedicated to that feature. This allows the developer to focus on developing the feature in isolation without concern to what’s happening with the rest of the project.
There are few rules or restrictions on how each developer works inside his own feature branch. He can commit changes as often, or as infrequently as he likes. As long as the feature has been defined to be small enough, it does not matter.
Each feature branch is named after the feature being developed and starts with the initials of the developer or developers who “own” the branch. As an example, if I need to update the homepage, my branch would be called `nh-update-homepage`.
We’re a small team, but we believe strongly in the value of code reviews. All of our feature branches are reviewed by at least one other member of the team before code is approved to be merged to master.
We use github pull requests to review the changes between the master branch and the feature branch. This way, only the changes made by the developer will be reviewed and changes can be made to prepare the code for delivery.
Github’s pull requests allow us to leave comments for each other, share knowledge of the work being done and have a great conversation on the code that will be available for us to review in the future. (Yes, we’ve gone back to our work later to determine why decisions are made. Documenting those in a pull request allows us to understand the decisions again quickly.)
After a branch is reviewed, the developer(s) who approve the request leave a comment saying ‘LGTM’ (Stands for Looks Good To Me) to confirm the code has been reviewed and allow future discussion.
Squash Merge with link to pull request
We don’t keep old feature branches and we don’t care how each feature branch was developed. A month from now, we’ll care only about the features that have been added, so we discard the details of how each feature branch was made.
We do this by creating a new commit on the master branch that represents all of the commits in a feature branch through a technique known as “Squash Merging”. This allows us to have every commit from a feature branched rolled up into a single new commit.
Inside the commit comment for the squashed merge commit we’ll include some text that Github recognizes to link/close the associated pull request. (Example: “Closes #38”) This allows us to review the discussion on each feature if we need to review in the future.
As another bonus, the squash merge means that the history of the project remains clean. Each commit in the master branch represents an entire feature after review, so if we do a “git blame” to see when a line of code was changed, we’re able to see the full context of the change, not see that it was changed because of a typo fix.
After the merge to master, the feature branch is removed and will never be needed again.
What about features that are too big for a branch?
Sometimes a feature is too big to be part of a small branch, but cannot be released to users until all of the associated tasks are complete. To handle this, we use rollout to include the feature being developed into the master branch but leave the new functionality disabled until complete.
This allows other developers to work with the new functionality without displaying it to users. Sometimes we’ll have both old and new code in the project at the same time which means we will go back and remove the old code after the new feature is enabled. This adds a small amount of overhead, but eliminates the headache of managing multiple long running branches just to support the addition of new features. It also maintains a clear view of the actual state of the project for all developers.
How do we follow this process?
While we can do every one of the steps above by hand. We’ve developed a tool called Git Reflow that automates much of this process to make sure we follow every step every time.
Here’s how it works:
git reflow start nh-branch-name to start a feature branch.
git reflow review to review a feature branch.
git reflow deliver when finished to deliver a feature branch to master.