All in the <head> – Ponderings and code by Drew McLellan –

Progressive Versioning

When you run a software-as-a-service web app, one thing you don’t need to think too hard about is software version numbers. You can roll out new functionality and fixes as soon as they’re ready, and as customers don’t need to make a conscious decision about updating. They’re just always on the latest version.

With desktop apps or on-premise software like Perch the version numbers take on a bit of a dual purpose. Primarily they’re a technical signifier of the code version, but secondarily they perform a marketing function. In order to encourage users to update (and perhaps pay to update) sales and marketing activity naturally hangs off the ticking over of software version numbers.

Big software companies have used different strategies over the years to try and decouple these two uses of version numbers. In the 90s, Microsoft went from using a version number with Windows 3.1 to using the year (Windows 95, 98, 2000, Server 2003), to the whacky (XP) and the nonsensical (Vista!) right through back to the version number (Windows 7, 8, 10). Apple used big cat names for Mac OS 10.0 through to 10.8, before switching to Californian place names, always accompanying the real version number.

One thing has become clear over the years; you can’t unlink software releases from their version numbers. You just can’t. If you’re versioning your software well, then the features and the version number are inextricably linked.

For the last six or so years, we’ve been using semantic versioning with Perch, which by and large has worked well. We’re currently on major version 2, and we drop new major features in at 2.x. All our marketing pushes are based around a 2.x feature release, in order to encourage users to come and get their free update.

Of course, we don’t put out new minor features every day of the week, and so multiple related improvements get rolled up into each 2.x release alongside whatever our headline feature is. It’s our chance to make a big push to encourage users to update, so we trying to bundle up lots of exciting looking features all in one go.

This does bring with it its own set of problems, however. Let’s remind ourselves how semantic versioning works:

MAJOR.MINOR.PATCH

The patch releases are supposed to be for bug releases only. That naturally places your development into a waterfall-style cycle of release and refinement phases. You work for a while on new features, release them, and then subsequently refine by patching bugs.

Stability Waves

The first issue this can create is what you could call stability waves. New features bring with them new bugs, and there’s also the potential for bugs to crop up in any parts of the existing codebase that are touched by the changes. You ship your new 1.1.0 version, and it has exciting new things but could be buggy.

Things begin to stabilise with successive 1.1.1, then 1.1.2 releases until the codebase settles down and the product is good and stable again. Just as you reach that point of stability – the crest if you will – you ship 1.2.0, the wave breaks and have to start all over.

The Big Reveal

The second issue stems from batching up changes and features to make a big release. Unless you have the rare ability to work on multiple features in parallel, you inevitably end up with a bunch of work that is complete and waiting for a Big Reveal.

All that time when those new features are ready and sat waiting, they’re not in the hands of customers. They’re not being used, and those edge-case bugs aren’t being found. Instead, you ship it all at once in a Big Reveal, hit all your bugs at once, and end up with a stability dip.

A different type of versioning

So what’s the answer? Surely this is the point where I offer a silver bullet to solve the problem once and for all. Sorry, but you’re out of luck. But here’s what we’re going to try instead. It’s what I’m calling progressive versioning, and it’s very similar to semantic versioning, bar the patch:

MAJOR.MINOR.PROGRESS

The key difference is that instead of starting a minor release with a result (a complete set of buggy features) you start with an objective. That objective might be something like v1.2 adds export functionality. The end goal might be to export to a variety of document formats.

You do some of that work – perhaps adding a PDF export – and ship it as 1.2.0. Immediately, any customers that need PDF export have something to work with. 1.2.1 might add MS Word export, and fix a bug with PDFs in Acrobat on Windows. 1.2.2 might add Excel export, and patch up a compatibility bug with for Open Office reading the Word files.

The key point is this. The 1.2 minor release is only complete once you’ve hit 1.3. There is no pressure to ship the full feature set in your x.y.0 release – that’s just the marker in the sand for the start of that minor feature.

History has taught us that releasing early and often is a good way to develop, and this builds a versioning structure to support that approach. In all other respects the semantic versioning compatibility principals still apply. Your minor release needs to plan out interfaces so that progress releases don’t break backward compatibility.

As for the marketing aspect, it still gives us a version to hang a message on. Version 1.2 features document exports! It may also give smaller and more frequent opportunities to market some of the smaller features that would otherwise get overshadowed in a launch.

Will this help? Honestly, it’s completely untested so far. We’re moving to progressive versioning for Perch and Perch Runway 2.9, which we’re starting on this month. It’s just an idea, and I’ll let you know how it goes.

One thing’s for sure though. There are only three hard problems in programming. Cache invalidation, naming things, and everything else.