Versioning mobile app releases like a pro
We all know that a project should have a version number. When you release new features, you should increment your version number, right? But exactly when in the development process is that? And how do you keep track? This article describes the simple strategy that I use across our mobile app projects at Control F1.
Although I’m writing about mobile apps, the principles apply to any software development project. So please read on, even if you’re not part of the mobile revolution.
Aims of versioning
Before getting to the when and how, let’s start with why. My main aims are that:
- Everyone should know what features are available in the version of the app they have
- Everyone should know what version of the app they have if they are reporting bugs or requesting support
When I say everyone, I mean anyone who uses your app, including internal testers, beta testers and end users.
What is a version number
A version number has three components,
1.2.13. You decide which to increment based on the changes you have made since the last version.
patchis for minor changes or bug fixes
minoris for new functionality
majoris for major changes, like a rewrite of the project
If you increment a higher component, you zero out the lower ones, so
1.2.13 would become
1.2.14 with a patch version increase,
1.3.0 with a minor version increase and
2.0.0 with a major version increases
Setting the version number in your project
In iOS it is set in the
Version field in the General tab of your app’s target properties.
In android it is set in the
versionName field in the gradle file.
Display the version number in your app
In order for everyone to know what version number of the app they have, you need to display it to them in your user interface. The home screen, main menu or an information screen can all be good places to put it.
Track which features go into a version
Start by creating a placeholder for your next release version. This can be as high-tech as a fix version in JIRA, or as low-tech as a note on a piece of paper.
As soon as you finish features or bug fixes, label them with your placeholder version. Don’t wait until you release, label them straight away. Again, you might use the fix version field in JIRA, or your might keep a list of fixed issues on your piece of paper.
Then to check if a given version contains a feature, you simply need to check the version for your feature against the version of the app. If the app version is greater than or equal to the feature version, it’s included. Simple!
When you create your placeholder, how do you know what your next release version will be? The answer is, you usually don’t, because it could be a major, minor or patch release. But it doesn’t matter for your placeholder, just increment the patch number. So if your last version was
1.2.13, your placeholder will be
1.2.14. And what if your release actually turns out to be
1.3.0? Well, the release version is still higher than the feature version, so you know that the feature is included.
If your app has never been versioned before, start with
Build a release
I’m using release to mean the process for packaging up an app to give to some or all of your everyone. For android you’ll create an APK, and for iOS you’ll create an IPA. When you build a release, you bake in your features, and you also bake in your version number.
Build your release when you have added a feature (or features) that you want to make available to some or all of your everyone — in my workflow that’s as soon as the developers want to make features available to the test team. To version the release:
- Decide whether this is a major, minor or patch change, and increment the version number accordingly
- Build the release
- Commit your version number changes and tag in source control with the version number
At this point you start over with creating your next placeholder version to record against completed features.
Create release notes for your users
For everyone to know what features are available in a given version of the app, you need to provide them with release notes. We make different versions depending on the audience, but as an example we’ll do the following.
- Internal testers get access to a full list of features and fixes with their release versions. That way they can check what is available to test in any particular release.
- For beta testers and users, we compile a non-technical, easily understandable report. They might not get every single release, so we make sure to include everything that has been added since the last version that was made available to them.
A note on accessing code for old versions of your app
In the not too distant past, another important aim of versioning was to allow you to access code for old releases. Releases were few and far between, and different versions of the same software were supported at the same time. Bugs had to be fixed and patched in a particular version, which made it important for you to be able to lay your hands on the source code for that release. Now that releases are frequent, and generally only the latest software version is supported, it’s very rare to go back to an old version. In fact I can’t remember the last time I patched a release.
But as long as you tag your release commit with your version number as described above, you can always get back to any version you need.
A note on build numbers / version codes
As well as a version number, iOS apps have a build number, and Android apps have an equivalent version code. This is a single number that identifies a particular build, and the app stores require that it must always be higher than the last one when you submit a build. They don’t have a special purpose in my workflow, so we simply increment them at the same time as the version number.
Other strategies and why I don’t use them
Build numbers / version codes
To indicate that a new release only includes a very minor change or fix, some teams might only increment the build number. This is sensible, but we don’t use it because version numbers increment automatically in our release process, and we’d have to intervene to stop that happening.
Tracking features in versions after you release
In my workflow, we create a placeholder version after the release, and we add versions to features as soon as they are developed. You could wait until after you’ve built the release, get its actual version number, and then go back and tag all the features that went into that release. I used this approach for a while, but found a couple of drawbacks. With my workflow, if you use a system like JIRA to track your releases, you can see how your release is shaping up as you go. You lose that if you don’t version features as they are completed. I also found it to be more of an administrative burden to wait until the release. You have to find all features that have been finished but not released, and you have to tag them all at once.
Incrementing versions after the release
In my workflow, the version number is incremented before the release is built. I have seen workflows where it is incremented immediately afterwards instead, in preparation for the next release. That’s not a good idea because you can have builds that don’t contain the features you expect them to. Let’s say you’ve incremented your version to
2.5.3 and you’ve built it to a device. Then you add more features and tag those, but the build on that device never gets updated. The app says its version number is
2.5.3, but it doesn’t contain the features it should. That can’t happen if you increment just before you build the release.
Alpha, beta, RC, GA etc
Sometimes you see releases labelled with the version number plus alpha, beta, RC (release candidate), GA (general availability) or similar. There are two reasons why I don’t use this. First is that it’s unnecessary in my workflow. When a release is built, it is sent for internal testing. If it passes internal testing, it will be made available to beta testers. If it performs well in beta testing it will be promoted to the app store. If it fails at any of these stages, it won’t progress any further and the process starts again with the next build. You can tell the status of the build by where it is in the process, you don’t need the tag to tell you. Second is that it adds extra work. It is the same build at each stage, and it would be pointless to re-build and re-version exactly the same code just to add a tag.
I’ve seen apps that use the last commit hash on a branch to identify test releases of an app. This uniquely identifies the release, but gives you no information about the order in which the releases occurred. For example if you have a feature that was added in version
1.3.4 and you have version
1.4.1, you know it is included. However if it was added in version
9a10ea01, you have absolutely no idea if it should be available or not in version
I’ve been unfortunate enough to work on projects that didn’t use any versioning, at least until after the testing stage. If you’ve ever tried to figure out why something isn’t working when it should be, or seen the frustration on a test engineer’s face as you realise that you accidentally gave them an old build, you’ll know where I’m coming from. If you haven’t, trust me, don’t go there.
In this article I’ve described what I want out of versioning, the strategy I use to achieve it my mobile app projects, and why I’ve chosen not to use some alternative approaches. I hope you have found it useful.