Hartley Brody

Minimum Viable Git Best Practices for Small Teams

logomark-orange@2xWhen I started as the first employee at Burstworks, the cofounders and I could easily hold the information about who was working on what at any given moment in our brains.

But as we worked on new projects and the scope and size of the engineering team grew, all of our code mostly stayed organized in one central repository:

  • Our high-performance ad server
  • Data Pipeline
  • One-off scripts
  • Nightly jobs
  • Everything...

While we generally weren’t working on the exact same files at the same time, there was still lots of stepping on toes. Having your git push rejected was a common occurrence.

Inevitably we had issues with merge conflicts, which lead me to send this tweet from our company account:

And so I decided to take a step back and think about how we managed our version control system at Burstworks.

I definitely didn’t want to come up with something heavy handed or overly-proscriptive. The goal was to come up with just enough process to grease the wheels, and not slow things down.

I did some reading, came up with some initial ideas and pitched them to the team. We iterated a bit and here’s what we came up with.

I should start out by saying that it’s nothing revolutionary or new. It’s what I would consider the Minimum Viable Git Best Practices™ for a small engineering organization.

Diff Everything

You should review every single change before you stage or commit something. Whenever print statements or “# don’t commit this” make it to production, it’s almost always because someone blindly ran

git add .
git commit -m "my git is bad and i should feel bad"

without diffing their changes.

If you’ve already added a bunch of changes to staging and want to confirm them before you commit, you can run

git diff --staged

or alternatively, run

git commit -v

to bring up a vim editor to edit your commit message, while also viewing the diff you’re about to commit.

Pro tip:
If that seems like typing a lot of commands to remember, make some aliases in your shell's profile.

Commits Should be Small and Frequent

You should be committing code whenever you have made a single logical change. This allows you to write concise but descriptive commit messages, which offers great context for others who might be reading through your code.

Committing small things, frequently make it much easier to handle bugs or other bad situations:

When did we push this bug? Oh I see the commit... but a bunch of other things changed too. What was being worked on here?

I need to roll back that commit but what else might break if I do that?

Here are some code smells or signs that usually mean you’re not committing frequently enough:

  • A 100+ line file is being committed for the first time
  • You’re changing more than 20 lines of a file in one commit
  • You’re only committing when you take breaks (i.e. lunch, end of day, etc)
  • You have trouble succinctly describing what has changed (see below section)

Pro tip:
Sometimes when you're in the zone, you end up making a bunch of different logical changes to the code base without stopping to commit each one. That’s okay: git add -p to the rescue!

Running git add in patch mode (sometimes called “partial” mode) lets you stage a few lines out of a file for a commit, leaving the rest of the changes to a file unstaged.

Git will automatically show you chunks of changes from the file, and ask if you want to stage them or skip them. There’s also an option to break down the current chunk into smaller ones for really fine-grained control.

Now you can take the huge refactor you just cranked through and break it down into smaller, logical commits.

Commit Messages Should be Semantic

Every commit message should describe why the code was changed – or what a change accomplished – at an appropriate level of detail.

You shouldn’t just use words to describe what parts of the code have changed – anyone can see that from reading the diff.

If someone wonders why a line of code was created or edited, the commit message should make it clear.

Here are some code smells or signs that you’ve writing a bad commit message:

  • The message is less than 3 words
  • The message is more than 10 words
  • Your message is too high-level (it’s hard to be too low level)
  • You don’t know what changes are being committed (see above section)

Pro tip:
Did you just make a commit with a bad message like "refactor" or "business logic"?

It happens to the best of us. Just use:

git commit --amend

which gives you a vim editor to change the last commit’s message.

Use Feature Branches

If you’ll be making multiple commits that are related to each other, they belong on a separate branch. One of the nicest things about using branches is that both Github and Bitbucket support Pull Requests which allow for a discussion about a collection of commits, before they’re merged back into master.

Using branches makes the merge into master feel very concrete and important. It gives you a chance to see all of your final changes while ignoring work-in-progress commits. It also means that the master branch is always ready to deploy, with no half-ready changes mixed into the code.

Signs you should be using a branch:

  • You will be committing “work in progress” changes to save progress that leave your application in a broken state and shouldn’t go to production
  • There will take multiple logical changes to the codebase that are part of a larger project
  • There will be several commits in a row that all depend on each other and should be in order

Signs you don’t need a feature branch – that is, it’s okay to commit to master:

  • You’re making a small change that fits nicely inside one commit
  • Bug fixes/hot fixes for fixing typos, etc
  • Previous/future commits won’t affect this commit

When you’re working on a branch, make sure you run git pull origin master frequently, (at least once a day) so that your branch doesn’t get left behind, and to decrease the likelihood of merge conflicts.

Once we switched to using branches, we found that we spent a lot more time reading each others’ code. This helped us learn from each other and gave us all a chance to recognize new or potentially troubling idioms and have a discussion around them, “in the code”.

It also allowed us to catch mistakes sooner and keep problematic code from being merged into master and shipped to production.

More Git Resources

If you want to learn more, here are some of the resources I used when coming up with these tips.

I’m hoping to avoid starting a flame war between different workflow models, but if you have constructive suggestions or more git power tips, feel free to leave a comment or drop me a note on Twitter!