April 25, 2014

I’ve been in software development for about a year now. In that time, I went from “Huh? What’s version control?” to having a working knowledge of Subversion, Mercurial and Git. Most of my experience came from my work terms at Desire2Learn, supplemented by my Operating Systems course last Fall, which required collaboration, and could have been nightmare had source control not been involved.

I’ve had several discussions with colleagues about practices that make our lives easier. As someone who is highly interested in driving process improvements, such discussions have great appeal to me. The ideas highlighted below have cropped up several times (enough that I’m writing about them!).

Think before you commit #

Before you commit, think carefully about the changes you made? Are they functional changes? Or are they decorative? Will they cause existing features to break? (If the answer to the last question is yes, consider branching - it has saved me from causing undue grief to others on multiple occasions!)

Before you commit, also think about what to put in your commit message. Commit messages should be meaningful, and, if possible, provide a gist of the changes. I like to think about it in this way: “If I return to this commit a year from now, will I be able to get an overview of what I did without going through the changeset?”

Refrain from using empty commit messages - as far as I know, Mercurial and Git prevent this by default. A few months ago, in response to a bug tracing and fixing request, I had to browse through a Subversion repository log devoid of any commit messages. It was an extremely tedious, and, as an amateur developer, a difficult task for me to understand the authors’ reasoning behind their changes. I was lucky, as I was able to seek help from said authors (who were still around in the organization) - the outcome would have been very different (and the solution potentially more time-consuming) had it been otherwise.

Commit small, commit often! #

Keeping commits small, consisting of logically grouped changesets helps immensely with code review. In my experience, it allows the reviewer to offer better critique and actionable feedback - a large number of huge changesets in one commit can potentially exhaust a reviewer, and consequently, cause them to overlook key changes.

I recently discovered another benefit of keeping commits small - it helps if you need to reverse a particular set of changes in the branch history. Small commits make it easier to perform precise, targeted reversals, without losing the changes that you would like keep.

In my experience, committing often helps if there are multiple developers (and testers) working in the same branch. Last summer, I paired with a developer as a tester for a feature he was working on. His small and frequent commits and pushes allowed me to provide him with feedback continuously throughout the development cycle. This resulted in a much more stable product, and also divided the quality assurance effort over the sprint (in contrast to performing all testing in the last couple of days of our week-long Agile sprint).

Keep refactors and logic changes in separate commits #

This practice is one I personally feel most strongly about. When fixing bugs in test-less legacy code, my colleagues and I found ourselves performing several (and at times, large) refactors (as part of the bug fix, to make code more testable, to reduce duplication, etc.). I found that key logic changes had a tendency of being lost (or overlooked at code review) if it was bundled in as a part of a large refactor.

I have a simple example to demonstrate. Initially, we have:

def foo(): statement 1 statement 2 statement 3

After a refactor and a bug fix, we have:

def foo(): bar() def bar(): statement 1 statement 2 statement 4

This is what the changeset shows (if the previous change was in a single commit):

def foo(): - statement 1 - statement 2 - statement 3 + bar() + + def bar(): + statement 1 + statement 2 + statement 4

Now this example is small, so it’s easy to visually observe the change from statment 3 to statement 4 . In case of large refactor, and several changes in logic, it is easy for the key code alterations to become hidden in the refactor. As a code reviewer, this makes it hard to isolate and inspect just the bug fix.

I realized that it was perhaps a better idea to separate the two types of changes. Not only does this ease the task of the reviewer, this also makes it simpler (as discussed previously) to reverse changes that are deemed unwanted down the line, without letting go of the some of the benefits gained from changes made in preparation for the actual fix.

Parting thoughts #

I have a feeling that, if adopted, these practices will provide greater gains for team projects than for solo projects. My focus for this article was definitely towards projects with multiple contributors, but I don’t see how following these ideas will cause impediments in your individual projects.

Don’t be a git, be good to your collaborators!

Agree? Disagree? Share your thoughts with me on Twitter @SammSid.

35 Kudos