by Matt Brennan

Heroku Version Infer

In the Apps team at the FT, we quite quickly ended up with 3 separate new Node apps running on Heroku, each with their own deployment pipeline and release schedule. Keeping track of what was released and fixed when was intractable. Manual version numbering was impractical. Gotta go fast.

I hit upon the idea of using something like semantic-release to automatically number our versions. We weren’t releasing npm packages, so not the thing in itself, but something based on the idea of using commit message conventions to guide version numbers. We quickly realised that semver major-minor-release was pretty much meaningless, and the idea got shelved.

A few weeks later I had a thought. A version number is nothing but a monotonically increasing sequence. It needs to increase if and only if there is a change to the code that’s running. It needs to be derivable from nothing but the source code and its Git history. Number of commits to master would fit the bill, but the number would quickly grow and become meaningless: “when was that fix released? In 2497?” “No, it’s in 2506, that’s not in production yet”.

Instead, I played around with the number of merge commits into master. It’s far less granular than gross number of commits. We have automatic deploys from master to our Heroku staging apps, and master is branch-protected on Github. That ensures that every release to staging has an associated merge, and every merge gets released. Or, put another way, the number increases if and only if there is a change to the code that’s running. It’s also comparable across environments. If production has a lower version than staging, you know exactly how many pull requests it’s behind by. If they’re the same, you know production is up to date and running the exact same code as staging.

I’ve encapsulated this in the npm package @quarterto/heroku-version-infer. It’s got a CLI designed to be run at the npm lifecycle script heroku-postbuild. It needs correct repository information in your package.json and it depends on the mostly-undocumented Heroku environment variable SOURCE_VERSION.

On its own, it just updates the version in package.json. I’ve got a suite of other tools to propagate that release version to other things that need to know about it: Github, Sentry, JIRA. They don’t depend on each other, but they do play nicely together.

We’ve been versioning like this for a couple of months now and it’s been pretty useful.