Paranoid Git workflow

In a few days I will rotate to another project at work, and imagine that big and heavily developed project, where there’s 5 devs in the team plus few devs hired by the client in overseas office. Such a setup very often leads to communication problems, no matter how hard we try to be lean and agile. It also leads to code quality decrease because of people rotation, especially when almost every newbie at the office is going through the project. And it’s not only my imagination, I already heard it so many times, that someone crashes the build, some crap has been pushed to production, etc. In that case I want to introduce something I called “Paranoid Workflow”, you know, for now as an experiment. So here’s what is it all about…

Don’t touch master!

First thing I’d like to add to the project is a simple init/setup script. BTW. if you don’t use setup scripts in your project, shame on you. This approach was mentioned many times, especially by devs from Github – if you have something to do in the app, you should have an init script. Don’t write readmes or other bullshit, automate everything. The script can be a bash file, or a Rakefile, whatever you prefer. I’m lazy, so will go with rake script here:

@@@ruby
desc "Initializes all the necessary things in the project"
task :init => [ :install_git_hooks ]

desc "Installs custom git hooks"
task :install_git_hooks do
  system "cp ./scripts/hooks/* ./.git/hooks/"
end

Simple stuff, you run rake init and our custom hooks gets installed to local git configuration. You may ask why to use a rake file instead of simple bash script doing this:

@@@bash
#!/bin/bash

install_git_hooks() {
    cp ./scripts/hooks/* ./.git/hooks/
}

main() {
    install_git_hooks
}

main

Or even one stripped out from the internal structure. Well, whatever you prefer. Mentioned project is in Ruby, and people working on it use different shells (zsh, bash), so rake works for all of them with no fuss. Anyway, going back to our hooks. What kind of hooks do you think I’m going to put there, huh? Of course I’ll not let anyone to commit directly to the master!

@@@bash
#!/bin/sh

if [ "$(git symbolic-ref HEAD 2>/dev/null)" == "refs/heads/master" ]; then
    echo "Sorry bro, you can't commit directly to master!"
    echo
    echo "Instead, you should mess around in different branch and then "
    echo "fast-forward merge your changes to master."
    echo
    echo "  git checkout -b feature_xxx"
    echo "  ... bunch of changes and commits"
    echo
    echo "Remember! Before you merge to master it's a good idea to send"
    echo "a pull request first! Merge your stuff only when it gets"
    echo "approved by the team:"
    echo
    echo "  git pull origin master && git rebase master"
    echo "  git checkout master"
    echo "  git merge feature_xxx"
    echo
    echo "Don't be mad, being a little more cautious is better than being"
    echo "ashamed of broken build."

    exit 1
fi

Script is simple, it checks if we’re currently in master, and if so then it doesn’t allow us to make a commit, displaying a comprehensive information instead. So why to block commits to master, you ask? First, to don’t allow pollute it, or prevent from breaking the build by pushing “small fixes”. Those small fixes are most often pushed without running tests, and that’s how shame broken builds reaches the project. Second, we want to encourage users to use pull requests as a code review tool. If you’re eager to have possibility to commit directly to master you could hack the script by adding some env variable switch, something responding to FORCE_COMMIT=1 git commit .... But I will not do it, because if you allow people to do something bad, they will do this for sure.

Code review

The most important part, though is the code review. Our code is hosted on Github, so we have possibility to use the best code review tool ever made – pull requests. But you know, it’s a pain when you commit something, then you have to go to the website, click trough the wizards etc. Remember, automate all the things. Install hub and do everything from the console:

@@@bash
curl http://defunkt.io/hub/standalone -sLo ~/bin/hub
chmod +x ~/bin/hub

Now you are able to submit a pull request:

@@@bash
hub pull-request "Merge me, please!"
hub pull-request -i 29 # Attach to issue no. 29

Remember to push your branch to the remote! Also you have to know, that when repo is in the Github organization, you have to explicitly say which head to use in pull request:

@@@bash
hub pull-request "Done with feature #12" -h organization:feature_12

Now let someone from your team review the code and merge it. Remember it’s very important to actually review the code, so that you can eliminate bugs and suggest better solutions. But be careful, though! Don’t hop into the waterfall. Working code now, even if ugly, is better than the perfect one tomorrow.

Summary

I encourage you to take part of this experiment and introduce described workflow in your project. Of course don’t be too paranoid, don’t use it if there’s few devs in your team, or in cowboy-coding-driven projects. Everyone else should definitely try it out and share his experience! I hope you gonna enjoy it.

Comments

blog comments powered by Disqus