Short and shallow thoughts on library versioning

Do you know that software versioning even follows superstitions, has political and cultural impact?

Table of Content

What is versioning?

The versioning is a practice in software development where we assign unique numbers or names to a particular state of software application. Example - macOS Mojave, Windows 10 or Lodash 4.17.15

Types

There are different types of release schemes. It can range from a sequence-based (SemVer), date based released (Ubuntu 18.04, for example, was released in April 2018), codename based (Windows Vista), etc.

How do you semantic version?

Version = Major.Minor.Patch (Example- PluralJS 1.1.0)

The semantic versioning follows an incremental number (positive integer) approach where you increment the number based on the significance of changes. The significance is categorized as Major, Minor, and Patch.

You increment Major version number when you have breaking changes, if you have backward compatible changes then you increment minor version and others fall under Patch version.

Additional tags may be used to extend the above format. These tags are usually pre-release versions such as alpha, beta, release candidate, etc.

You start your initial development with the Major version as 0 and you go till you release the first stable 1.0.0 version. Hereafter, any change in code release should increment version based on the nature of change.

You can add a tag for pre-release versions such as 1.0.0-alpha. The format follows hyphen immediately after the patch version for the tag. The tag is ASCII alphanumerics [0-9A-Za-z-] and separated by a dot. Example - 1.2.0-beta.1.0.1

Wait! We even have a provision to add build metadata. This is only for information purpose and doesn't count in versioning. You can append a + sign after the tag again in [0-9A-Za-z-] and hyphen format and can use dot to separate out metadata. Do you know what is effective use of build metadata in versioning? Excited to hear back from you on Twitter or email. Example - 1.0.0-beta+git.sha.5114f85

Few more examples -

  • 2.0.4-gamma.2
  • 6.7.4-kalpesh.1.22.35
  • 1.0.0-alpha+001

npm tags

When you publish an npm package, by default it updates @latest tag and your package published with @latest tag. The default install will be with @lastest tag too.

You can specify npm tag (using —tag flag) such as beta or rc when you publish a package. Note that npm tags are different than SemVer tag. SemVer tag has alphanumeric format but npm tags are usually short to write and understand.

You can have package version as 3.4.2-alpha1.2.0 (SemVer compliant) but your npm tag for this version would be alpha only and developers will download it as npm install fancyrocket@alpha.

Side note: npm publish command will include everything from the project except files or directory mentioned in .gitignore or .npmignore. If both files exist, npmignore will take precedence and if the file mentioned in gitignore but not in npmignore then it will be included.

Further readings

When should we bump the version?

You should bump the version after your build is successful. This means you will bump the version in your CI (Continuous Integration) or manually in package.json file after building and before publishing. If you bump the library version in your CD (Continuous Deployment) and if the deployment fails then you will have the latest code with the old version.

Bump a package version

npm follows SemVer for versioning packages. You can use npm version <SemVer> | <keyword> command to bump the package version, create git tag, and commit it (all these combined in a single command). If you pass -m flag then it will create a commit message for version commit. The %s will be replaced with the latest package version.

npm version patch -m "Upgrade to %s for reasons"

Keywords - patch, minor, major, prepatch, preminor, premajor, prerelease

If we add preversion, version, or postversion in the script then they will get executed when we run npm version command.

"scripts": {
  "preversion": "npm format && npm test",
  "version": "npm run build",
  "postversion": "git push && git push --tags"
}

The above example makes sure that formatting and testing are completed and proceed only when all the tests are passed. Then it runs your build script. Finally, you will push the code and git tag to the remote repository. You can use postversion to perform any cleanup task too.

Why do software applications mention their versions?

It can help the customer support executive to understand what version of software the user is running and advise them based on it.

Amazon India
(Click on image to open Amazon India app screenshot)
Amazon India app screenshot displaying version number 20.11.0.300 on bottom left
Dunzo
(Click on image to open Dunzo app screenshot)
Dunzo app screenshot displaying app version 3.13.0.0 on bottom left

Further readings npm version

Git tags

Git tags allow us to mark or label specific commits. The tags are different from having a git branch. The difference between a git tag and branch is that the git branch can have more commits afterward but a git tag is associated with a single commit. There are two types of tags - Lightweight and Annotated. The annotated tags have more information such as commit checksum, timestamp, author name, email address, etc. The lightweight tags have only commit checksum information stored.

Creating tags

# Annotated (-m/-a/-s flag is used)
git tag -a v1.0.3 -m "add test cases for plural grammar"

# Lightweight tag
git tag v1.4-beta

# Tag a specific commit you made in past
git tag -a v1.2 9fceb02

Listing tags

git tag

# Output
v1.0
v2.0

Pushing tags

The git push command won't push the code to the remote. You need to explicitly push the git tags to the remote server. It is simple and the same as git push.

git push v3.0.9 # Single tag push

git push --tags # Push all the available local tags

Checking out tags

It is dead simple and the same as checking out a branch.

git checkout v1.1.0

# You will see after running the above command.

Note: checking out 'v1.1.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 7f735b1 feat(main.js): add count parameter to pluralize word

Why do we need tags?

Git tags are commonly used to mark a release or an important commit. The primary difference is that you can make further commits and modifications in the branch whereas once you tag a commit, you can only delete it but you can't make future changes. It is helpful when your teammate wants to run a specific version of your application. They can run git checkout v2.3 and boom! They can experiment and play with v2.3 state of your application.

Consider a 4 member team is working on building a feature that would allow the user to delete their tweet within 60 seconds of publishing. They checkout a common branch from master called tweet-delete and all the 4 members work on that. At the end, when the feature is completed the team merge (squashing all the commits into single) all the code from tweet-delete branch to the master branch. When it is time to release the feature, they can simply add a tag say @feature/tweet-delete and push the tag along with code.

Now, if a cross-team member wants to check the behavior then they can directly checkout git checkout @feature/tweet-delete branch and see the feature right away.

That's the use case I can think from git tags.

Further readings Git - Tagging

Cultural, Superstitions and Marketing Gimmicks

  • We always consider the version 1.0.0 milestone.
  • The software versions are bumped to convey that product/library is more matured and sometimes to show the customers that we match with our competitors. If your competitor is selling v10 and your software is still selling v6 then there might be different perceiving thoughts on your software.
  • Microsoft has skipped internal version 13 when they launched Office 2010 (kept internal version 14 instead of 13). The last version Office 2007 had internal version 12.
  • The graphics design software Coral has marketed their version 13 as X3 (Roman number 10 and 3)
  • The SUSE Linux OS has released their first version at 4.2. This refers to number 42 which is "the answer to the ultimate question of life, the universe and everything" mentioned in The Hitchhiker's Guide to the Galaxy book.
  • Due to perceived marketing difficulties in the Chinese market, Maximo (Asset management software) moved from Maximo Series 3 to Series 5. The number 4 is associated with "death" in China and other East Asian Nations.

Further learnings

  • [ ] GitHub Actions
  • [ ] Automate versioning using semantic-release library

Resources


Spotted a typo or technical mistake? Tweet your feedback on Twitter. Your feedback will help me to revise this article and encourage me to learn and share more such concepts with you.