Clean Commits

By Chris Rittersdorf on 09 01 2011

Clean code is important in an agile environment. It's courteous to leave clean, expressive code for the next developer who has to dig through it. When code is clean, it effectively communicates its intent which helps others in easily making changes to it. This same courtesy should also apply to your source control.

Before starting at Mutually Human, I was a Git commit cowboy, always shooting from the hip. I'd commit as often as I'd save a file. I'd write cryptic commit messages. I never gave pause to think about what I was committing. After pairing with Zach Dennis on a project, I learned new techniques that have improved my code commits. By using these techniques: tiny code reviews, meaningful commits, clean commit messages, and using @WIP commit messages, commits can be useful to the entire team.

The Tiny Code Review

At Mutually Human Software, we do not have formal code reviews. We pair program extensively and treat this practice as our code review. Before committing, we leverage GitX, a Git GUI, to help us think about the changes that we've made.

When we've finished developing a feature we open GitX, and navigate to the "Commit" view. This view allows us to see the changes we've made to individual files since our last commit.

inspire (branch: blogging_example)

Lines in red have been removed in this commit. Lines in green have been added to this commit.

We use these differences to take pause and think about the code we're about to commit. If it's the first time we're evaluating the changes for this commit, we will re-factor. Any code that's not DRY gets consolidated. Any code that's not expressive gets re-written to make more sense. We can make these changes with confidence because we have tests that define how our project should behave. We then repeat this process until the code has been satisfactorily cleaned up.

Meaningful Commits

When we are finally ready to make a commit, we do our best to make it a meaningful one. There are two kinds of commits we commonly encounter: large feature-encompassing commits, and smaller bug fix or copy change commits.

It's easy to make a feature-encompassing commit be meaningful. Features inherently carry with them a definition of how system behavior will change. They also include customer goals. This type of information can clue developers in as to why the change was made and should be included in the commit message.

Giving meaning to smaller bug fix or copy change commit can be trickier. Sometimes you can't specify much more than "Fixed typos" in the commit message. This can be problematic. For example, say you are a developer that added a test for a module. A commit message that reads: "Added tests." does not provide usable information to anybody but yourself. Specifying why the test was added would be better:

Added tests for the Contacts

* Replicates 'ContactNotFound' bug
* A fix still needs to be implemented

Before crafting a commit message about a small commit, take a minute and put yourself in the shoes of a developer seeing this change for the first time. Would you understand what code has changed in this commit? Would you know why the code changed?

Clean Messages

When committing changes, we follow a common message format. The following example is from one of our latest projects:

No indication visually that there is a difference
between public and private contacts.

* Added badges section for each contact.
* Added lock if the contact is private.
* Added tooltip explaining badge.
* Added toolitps mixins for tooltips.

[contributes #124421]

The first line is a concise summary of the changes that we're making for the feature. Think of this as the headline of a newspaper. It should gain the reader's interest, give them some clue as to what the commit encompasses, but not contain too much information. This helps to identify specific commits while scanning through the history of commits.

On a new line, we include a list of important details for the commit. It helps to separate each detail with some kind of delimiter. We use the asterisk "*". These details should also be terse. Some points of interest might include refactorings, edge cases that were identified, or test coverage that was added.

Finally, we add a reference to the feature or bug. The "[contributes #124421]" line from the example is an instance of this. In our case we use a Pivotal Tracker Story ID. By navigating to the feature in Pivotal Tracker, we can find detials about the feature that might not be included in commit messages. A reference in the message also serves to tell us what features are on which branches during integration. Having references to features and bugs, can save your sanity when trying to merge two or more topic branches when it comes time to deploy.

Using @WIP

If we're not finished with the feature we've been working on by the end of the day, we will make a temporary commit with "@WIP" prepended to the message. This signifies that our commit is a "Work in progress". When we finish with the feature, we collapse all of our @WIP commits using the "amend" feature in Git. This allows us to erase the previous @WIP message from our temporary commit and replace it with a meaningful one. All of our work for a given feature is encompassed in one commit.

inspire (branch: blogging_example)

When you have clean commits, your fellow developers will thank you. Who knows they may even return the favor. Courtesy is contagious!