Take me HOME
Crazed(Sanity)
Versioning Projects and Subversion
Tuesday, July 09, 2013 12:05 PM

Version numbers are that magical, though somewhat mysterious, number that seems to be attached to all software products.  Sometimes they're one big long number, sometimes they're dates, and still other times they're a combination of seemingly meaningless numbers, letters, and dots. It all basically boils down to a need for developers to know what features (and bugs) are contained in that particular piece of software.  As bugs are patched and new copies of the software are released or existing installations are patched, developers need a way of knowing exactly what is in that installation.  Without this magical number, developers would have to contend with a possibly infinite number of installations whose feature sets are simply unknown.

I have several projects that whose source is controlled in SVN via SourceForge.net Git via GitHub.  I have developed a system of handling version numbers and releases that I would like to share today.

Version Numbers

Handling version numbers is more complex than just putting a series of numbers together with dots.  There are more than a few software developers that endure a hellish frustration of lining-up version numbers for various required libraries for their projects in order to make it all work properly.  The most important part about giving each version of your software a unique, serialized form: every new release gets a higher number than the previous one.

Determining what version number a particular project should start with really isn't terribly important.  If it is a brand-new project, I recommend starting at version 0.1: this indicates that it is new and still under construction; version 1.0 should be the first stable version recommended for the public.  Of course, there have been many very functional pieces of software that were very functional prior to making that "magical" 1.0: it is up to you (and/or your company's management/marketing departments) to determine what versioning scheme works best for you.

My methodology involves two main elements with two optional elements.  The version number layout is major.minor(.maintenance-typeNUM). The first segment is the major version number: in a 1.0 release, the major version is 1, and the minor version is 0; the optional maintenance version is left out and assumed to be 0, and the type+NUM is left out completely.

Major Version Number

This number for me starts out at zero for the initial inception of a project.  If it's an existing project that is just receiving it's first official version number, I generally give it a much higher number, or start out with 1.0 (a new version would then be 2.0).  This could also be set based on the number of years it's been running or the number of major revisions you believe it's been through (in case of the latter, I suggest adding one just in case there was one you can't recall).  If it starts higher than 1, I recommend keeping it below 10 as higher version numbers seem to confuse and worry people.

Changing the major version should be a fairly rare thing and involve massive backend and frontend changes.  Where upgrades are concerned, this generally involves a more complex manner of reloading data and "simple" modification of existing data (i.e. database stuff) simply isn't feasible.

Minor Version Number

Anytime the major version number increases, this should switch to zero (i.e. "1.2" to "2.0"):  that seems pretty obvious, but there are some that simply don't grasp the relationship between the two numbers.  I generally increment this number when a large number of new features are waiting for implementation: the new features are all added into this release (where appropriate).  Frequent minor version upgrades seem to generally be accepted as alright, though I would recommend leaving a good amount of time (i.e. at least a month) between these releases.

As the name implies, this is for minor changes.  If there is a database schema change, that warrants a new minor version number, though generally that change is coupled with a few other new features and changes.  Upgrading from the previous minor version number (as long as the major version remains the same) should be simple and seamless.  If there's a change that makes a release incompatible with previous versions then it may need to just become the next major release.

Maintenance Version Number

This is the third portion of the version string, i.e. the "13" in "1.4.13".  Major releases generally leave out this number, so version 1.0 actually has a maintenance version of 0 initially (i.e. 1.0.0).  As with standard numbering this one is reset to zero anytime the major or minor version is incremented, though sometimes this number continuously increases regardless of major/minor versions: when it is used as a "build number," it is sometimes associated with the last commit version from the SVN repository from which the release was generated on (i.e. an update from "0.9.1395" to "1.0.1500" to "1.1.1985" is okay).  Frequent releases with updated maintenance versions is perfectly acceptable as it shows that fixes are being implemented in a steady fashion (just be careful to make sure the customer understands that this means "bugs are getting fixed quickly" and not that "this product has a lot of bugs that we are constantly fixing"; the difference between the two statements is vast in the mind of the customer).

Once again, the name implies this number is updated for maintenance purposes only.  Only minor tweaks and code changes should be involved, and absolutely NO schema changes or major rewrites should be allowed.  This can involve fixing some items in the database or configuration files, and should always be backwards-compatibleIf there's a change that makes it non-backwards-compatible, you're looking at a MAJOR version release.

Release Type

Releases are occasionally made for the purpose of testing the system:  for this we have release types: when I'm working on upgrading from 1.1.x to 1.2, I go through the following cycles:  1.2-ALPHA1, 1.2-BETA1, 1.2-RC1, and then 1.2.0. 

  1. In the ALPHA stage, code is very unstable.  Any new release (i.e. 1.2-ALPHA2) could potentially introduce a fatal bug into an existing system and should therefore only be used by developers and testers.  Use at your own peril (and make backups of everything).
  2. BETA happens after ALPHA, and is where the code is mostly stable.  Upgrades are backward compatible, and only minor bugs might still exist (new bugs should mostly be a thing of the past at this point); minor feature implementations can still be added, but I frown upon them.
  3. RC, or "Release Canditate" versions, are built in preparation for release:  this is built for testers and those die-hard users that are really interested in new features. A feature freeze should now be in effect, and new RC releases are made only when bugs are found and fixed. RC releases should be few and far between, as any rapid-fire releasing at this point will cause potential (or existing) customers to worry about the stability of the code base.

Example Version Timeline

Note: Developers would implicitely be testing all versions, even those that don't list them.

Color key: Development only, testerseverybody

Version
Explanation
0.1.0-ALPHA1 Initial release, probably doesn't even work.  At all.
0.1.0-ALPHA7

Second development release: ALPHA2-6 were unusable.

0.1.0-ALPHA8 System is mostly working... hopefully the developers see it that way.
0.1.0-BETA1 First public release: no issues from developers, some daring testers might consider using it.
0.1.0-BETA(...) Release 0.1-BETA1 through 0.1-BETA12: lots of bugs found.
0.1.0-RC1 First release candidate!  All known major bugs and security issues resolved.
0.1.0 Full initial release.  It is usable, seems to be bug-free.  The 0.1.x version means it has limited functionality
0.1.1 First maintenance release, fixed bug xyz which caused a potential security issue.
0.2.0-ALPHA1 Begin work on the 0.2.x series, with lots of new features.
0.1.2 Fix issue (blah), found while revamping code for 0.2.x series.
0.2.0-ALPHA3 Found lots of bugs and squashed 'em all.  ALPHA2 had a critical issue, so it was never released.
0.2.0-BETA1 Codebase has solidified, released to developers and some daring individuals.
0.1.3 Found issue (blah) after revamping code in 0.2.x series.
0.2.0-BETA2 Found issue (blah), already backported to 0.1.x
0.2.0-BETA3 One more issue (blah), should be ready soon.
0.2.0-RC1 Developers and some daring testers said it looked good, this should encourage wider testing.
0.2.0-RC(...) (lots of versions fly: new releases built whenever all current bugs fixed)
0.2.0 Full public release.  All major issues and security problems fixed.  Developers & testers have given their blessings.
1.0.0-ALPHA1 Begin work on full version, with tons and tons (and tons) of new features and usability improvements.
0.1.4 Fix issue (blah), backported from 1.x trunk (nasty security bug)
0.2.1 Fix issue (blah), backported from 1.x trunk (nasty security bug)
1.0.0-ALPHA(...) (lots of features added, lots of bugs squashed, lots of improvement)
1.0.0.ALPHA82 Most bugs fixed, close to getting to BETA status.
1.0.0-BETA1 All security issues and bugs fixed.  Testers welcome!
1.0.0-BETA(...) (plenty of bugs & usability issues found & fixed; new release built whenever all (or most) issues fixed)
1.0.0-BETA132 Lots and lots and lots of fixes and interface fixes, many usability tweaks.
0.2.2 Backport security fix (blah), doesn't affect 0.1.x series.
1.0.0-BETA133 Security fix (blah).
1.0.0-RC1 All security issues & bugs fixed.  All developers and testers gave blessings.  Should be good to go!
1.0.0 Full public release!  No issues brought from RC1, so this is essentially a copy of the release candidate.

 

Storing Version Information For Your Project

In all of my software projects, I specifically store version information in a single file.  Any time a new release is built, that file is modified (first).  An example of this file's contents would be:

VERSION: 1.0.0-RC1

PROJECT: cs-project

There are a few very important reasons to do this that are specifically related to Subversion: I'll cover them in the next installment.  An important reason that is unrelated to SVN is for comparing code version to the database version. 

"What is this 'database version' thing you talk about?"

In order to lineup code features with database layout, it is important to store a version for code and another version for the database.  Upgrades can easily affect the database's layout (schema), or bug fixes might automatically move data around in the database to fix the problem.  By creating this dual-version, the applications can now automatically perform scripted upgrades that can affect the database.  Pull a new version down, and the first time the code is executed, the upgrade system can detect that something has changed and run scripts that will make things friendly to the updated code... but I'm getting ahead of myself: if you're interested in this topic, take a look at CS-WebAppLibs, which has a built-in upgrade mechanism (it is an extension of CS-Content).

Stay Tuned...

That was a lot of stuff to think about.  Are your eyes twitching a little?  Mine are.

In the next installment, we'll talk in depth about the code repository's role in all this, and why I have chose Subversion to fill this role.  We will cover:

  1. Basic Repository Layout
  2. Version Subfolders
  3. Updating Projects Directly From Subversion
  4. Building File Releases From Subversion

I reserve the right to update and/or completely change this page in the future.