Last night I gave a Subversion presentation for the Refresh Baltimore group. The presentation ended up being a bit more of an informal dialog due to time constraints, but that was actually great. I rather enjoy talking with people rather than at them, when appropriate. The audience seemed to be largely front end developers, but many were very new to source control in general and, of course, Subversion in particular.
After the meeting, I thought I’d write a bit about how I use source control within my workflow. It’s the personal experience that folks in the group – myself included – seemed most interested in. The terminology I’ll use will be Subversion-centric since that’s my tool of choice (for the moment), but the philosophies, methodologies and processes should be comfortably analogous to other versioning tools.
Before getting started, it may be helpful to understand, at a high level, how I use the repository.
For my purposes, I’ve adopted the so-called “branch when needed” philosophy. Following this approach, all day-to-day work is done on the trunk. Most of my changes are reasonably small (or can be completed in small, iterative steps) so this is an easy and logical convention to follow.
At some point or another, though, most projects require some level of refactoring. Then there’s the need to add some large feature whose tentacles may stretch across several of the project’s subsystems. These types of changes, indeed any change that can, will or even threatens to destabilize the trunk for any period of time, should be made in a branch.
Standard development tasks can continue along the trunk without concern for the instability that exists on the branch; changes made to the trunk can be merged into the branch so that the branch remains sync’d with the latest code base and, when the destabilizing development is complete (and stable), the branch can be merged back into the trunk.
My Repository Template
Understanding how I use the repository, it may also be helpful to see what my repository looks like before I commit any code. I have a template repository that looks like this:
At the top level, I’ve chosen to stick with the ubiquitous trunk, tags and branches structure. Beneath that, my structure is more customized and meets my needs for both product development and traditional web development. Here’s a quick breakdown:
Any branches of the trunk, or of another branch, created for the express purpose of developing against that branch. Branches created for refactoring or feature development, as outlined above, should be created in this directory.
Maintenance branches are created “just in case”. More on this below.
Tags, for me, are strictly read only and offer a historical snapshot. They may be deleted if they’ve outlived their usefulness, but deletion is rare and updates are unheard of. Tags should remain exactly as they were created and they are created to “pin” the code base at a given milestone. My workflow includes build and release milestones so there’s a tag directory for each.
Again, more on this below.
This is pretty much what it sounds like. When deployed, the trunk/html/ directory is the web root (assuming a web application). Any file that needs to be accessible by URI belongs somewhere in this directory.
My preference is to keep my class definitions outside of my web root. It makes me feel like my business logic isn’t quite so exposed. That may not be the case, of course, but every little bit helps, right? Using my own site as an example, the trunk/classes directory might contain subdirectories that follow the familiar java classpath pattern: org/robwilkerson/data, org/robwilkerson/util, etc.
The trunk/_meta/ directory contains, well, pretty much everything else. It might include the PSDs from design comps, any documentation files (ERDs, use cases, etc.) or anything else. It will always include the DDL scripts used to create or update my database(s) and my Ant build scripts.
This directory is never deployed to any server beyond the development server and should not contain any files or resources required by the application at runtime.
Everything above is about getting started. This is about working. The key to a useful source control tool is committing changes. For optimal results, commit frequently, commit logical changesets and keep those changesets small.
The more commits you do, the richer your change history will be and the more granular your rollback options will be. When working through bug fixes, fix and commit one bug at a time. Avoid fixing twelve bugs and committing everything in one push. How will you know which changes were made to fix which bug? You’re trying to establish a meaningful timeline and to provide accountability for each change.
While trying to keep your changesets small, though, use good sense. Don’t commit a half-baked change for the sole purpose of keeping the number of modified files low. There are no hard and fast rules on this, but it should feel pretty intuitive once you get started.
When committing changes, it’s important to provide meaningful commit messages. Some changes may require a sentence, others may lend themselves to several paragraphs. When I’m committing changes, I try to write my commit message so that a log view shows why I made each change rather that what the change was. Subversion has a pretty nice diff tool that will happily tell you what was done. The question of why can be much more elusive.
My rule of thumb is to write one sentence about what I did and as much verbiage as I need to describe why I did it.
In addition to development branches that are created whenever they’re needed, I also create a maintenance branch each time I deploy a project to production. I consider this a “release” and I like to have a maintenance branch for that release.
The reason for the maintenance branch is that while development on the next version of the application continues against the trunk, the maintenance branch sits ready if a nasty bug is found in the version that is currently deployed. The bug can be fixed in the maintenance branch, redeployed with only those changes (not the changes that are being made on the trunk) and, once fully vetted, those changes can be merged into the trunk so you don’t have the same problem in the next release.
In addition to “releases”, I also have a concept of a “build” (these are not new concepts, of course, I’m just saying that I use them in my process). Most deployments are staged. Development happens on a development server – in my case, a sandbox on my personal machine – before the code base is pushed to a staging (or QA(Quality Assurance), testing, etc.) server for integration testing and/or UAT.
Each time code is pushed to a non-development environment, I create a build tag to provide a snapshot of the entire code base at the revision from which the build was created. These can be useful from time to time, but are rarely critical. Personally, I just like being able to see the deployment path in one place. I often delete build tags when I feel like they’re no longer necessary.
When a build is being deployed to production, I create a release tag at the same time I create the maintenance branch. The release tag is the “golden snapshot”. Since it marks a release, it is immutable and I’ve never deleted one.
So that’s how I put Subversion to work for me. I’d be interested to see how others make Subversion – or source control in general – work for them.