Versioning is an important and one of the rather tricky aspects of software development. Simply put, versioning assigns a unique number that identifies a specific package or release. This post will look into a popular versioning method called Semantic Versioning.

What is Semantic Versioning?

Semantic Versioning is a popular versioning scheme that mainly uses a three-part version number. The version number is in the following format (there are pre-release versions as well, which we will look into in detail later):

MAJOR.MINOR.PATCH

For example, if the version number is 2.3.54, the individual parts would mean:

  • 2: Major version
  • 3: Minor version
  • 54: Patch version

The rule of thumb when it comes to incrementing these numbers is:

Major version

The major version number is incremented when you make incompatible changes.

Examples:

  • You removed an entire public class from a NuGet package
  • You changed the parameters that the API endpoint accepts

Minor version

The minor version is incremented when you add new functionality in a backwards-compatible manner.

Examples:

  • You add a new endpoint to a public API
  • You add a new parameter to a method with a default value so that old consumer can still call the method without breaking it.

Patch version

The Patch version is incremented when making backwards-compatible fixes.

Examples:

  • You fixed a bug without making backwards-incompatible changes

Pre-releases

In addition to the major, minor and patch versions, we may want to use pre-release versions. This would indicate that the product is not finalized. Examples of pre-release versions:

2.0.0-alpha

2.0.0-alpha.1

2.0.0-beta

2.0.0-beta.1

2.0.0-beta.2

2.0.0-rc.1

All the examples above are pre-release versions and are ordered from the lowest precedence to the highest one.

Please note semantic versioning has no knowledge of the words “alpha”, “beta”, or “rc”. The comparison is purely made by alphabetical order for non-numeric versions.

Build metadata

In addition to all the release and pre-release versions, we can also use extra metadata by using a plus sign (+) as a separator. This part is not used in precedence calculations and has informational purposes only. The metadata that follows the plus sign can be a series of dot-separated identifier lists.

For example, a version number with build metadata could look like this:

2.1.5+20220531 // Append the date of the release

1.8-beta+sha.a4b5d6 // Append a hash value of the package

Refactoring

How about refactoring? It’s not a significant breaking change, and you don’t add new functionality. Also, it doesn’t count as bug fixes. It might help prevent bugs from being introduced in the future, but it doesn’t strictly count as fixing anything.

We can find the answer in the Semantic Versioning specs:

Patch version Z (x.y.Z | x > 0) MUST be incremented if only backwards compatible bug fixes are introduced. A bug fix is defined as an internal change that fixes incorrect behavior.

So if you change the code for whatever reason, you must at least increment the patch version to maintain the uniqueness of the package/release.

Front-end Versioning

A common question and debated issue is how to version front-ends. The Semantic Versioning specification is all about “API changes”. API in this context can refer to a HTTP API or a package (NuGet, npm, Maven etc.).

Front-ends are consumed by end-users, and they are not consumed by other software.

By saying not consumed by other software, I’m not counting the web scrapers. When you develop a front-end page, you don’t make a contract with an external tool whose goal is to scrape data from your page. Most likely, it happens without your consent. Therefore, if you make a change in your markup that breaks web scrapers, it doesn’t constitute breaking change in an API.

Defining a “breaking change” in a front-end is not easy. For example, if you move functionality to another page, it might be seen as a breaking change as some users might fail to find the new location of the functionality. Does this mean that you should increment the major version? If you did, what would that mean to the end-users? They still need to use the application/website like before. When interacting with actual human users, version numbers don’t mean much.

Users don’t care about your versions

The only exception I can think of is a complete project overhaul. If everything changes so drastically, you may choose to refer to it as version 2.0 of your application.

Other than that, I’d argue the best way to handle changes in a user-facing application is by leveraging changelogs.

When a user logs in, you can display them a nice little pop-up and briefly explain the key things that changed. Some more complicated changes might need some interactive walkthroughs etc. The main point is, that a user doesn’t want to or need to know that you deployed 2.10.24 version of your application. So show them how it affects their experience and leave the technical details out.

Conclusion

Semantic Versioning is a very popular versioning scheme as it’s simple and flexible. Some aspects are debatable whether or not it should be used, such as front-end versioning, but this doesn’t mean that it does a good job most of the time. We also discussed how to handle versioning in user-facing applications.

Resources

Categories: programming

Volkan Paksoy

Volkan Paksoy is a software developer with more than 15 years of experience, focusing mostly on C# and AWS. He’s a home lab and self-hosting fan who loves to spend his personal time developing hobby projects with Raspberry Pi, Arduino, LEGO and everything in-between.