Version Control: The Big Picture

During my most recent software development project, which I’ve already blogged about extensively, I finally learned to use a version control system (VCS from here on) right.

To fully understand this, you have to know where I was coming from. I learned to program in the early-to-mid eighties, and never even heard of a VCS until about 1998, when I picked up a copy of Visual C++ 6.0. I couldn’t see much use for it — I was archiving a zip-file copy of the source code for my software at every version release, and it seemed to me that VCS did the exact same thing.

At that point, I viewed the “working copy” of the source code as sacred. I’d quickly learned that whenever I made a change to it, I had to be able to reverse it if it didn’t work out, so for small changes, old code was just commented out and left there — that, and the zipped-up backups, were my VCS. It made for some messy code, because I rarely went back to remove the old commented-out stuff, but it worked.

When I sold my earlier software company in 2003 and went to work for the new company for a while, I was shocked at just how casually developers at the new company treated source code. They made changes to it willy-nilly, without leaving the old code intact! What if they screwed something up? The five-minute introduction to CVS that I got explained how to check in and check out source code, but nothing about the big picture of how or why to use it. To me, it was still just a glorified backup program.

Fast forward to a few weeks ago, when I started making some extreme source code changes to a large twelve-year-old project. That’s when I started really learning how to use version control.

The first lesson: the working copy is temporary. The sacred version of the source code is the one in the version-controlled repository, and if you’re using it properly, you can always fall back on it if your changes have introduced problems. You can also use it on multiple machines if necessary, with no problems, the source of a future compile-speed-related blog entry.

There’s a corollary to this: check in early, check in often. Make small and focused changes, get them working, and check them in as soon as they’re done. That way, if a change introduces a bug, you can easily locate the exact source-code alterations that caused it. And if all else fails, you can easily throw it away and revert to the last checked-in copy without losing valuable work. If I’d known this a few weeks ago, and practiced it, I could have saved several days of debugging huge check-ins when they later turned out to have problems.

And finally, if you have to make large, sweeping changes that must all be completed before you can see whether they’ll work, make a separate branch until it’s finished. Then you can keep checking in the smaller changes as they’re completed, even though they haven’t been tested, and all the check-ins on the main branch are known to always contain good working copies of the program. With a modern version-control system like Git, you can easily merge the branch into the main code once it’s done.

Now that I see the big picture, VCSs make a lot of sense. I’ll be using mine a lot more in the future, and quite differently.