Give semantic to your Software (1/2)

Software versioning exists for a long time, and most enterprises use Versioning to identify their software. We can see Versioning applied into a desktop application, web applications, libraries, APIs and other applications, and with Versioning we can correlate features and issues to a version.

From the end user’s point of view, the person who uses the application, the versioning goal it’s to correlate issues and features to a version. But from the developer’s point of view, the goals are broader.

In application maintenance, the versioning correlation helps to identify in which version the issue exists and correct it. It is as straight forward as this. On the other hand, in application development the scope of the versioning is wider.

In software development, while we are the producers of a versioned artefact (the applications themselves), we are also consumers of versioned artefacts, such as APIs and libraries.

As to these last ones, software developers dread it so much that there’s a kind term for them: “dependency hell,” which comes in different flavors:

  • Long chains of dependencies
  • Conflicting dependencies
  • Circular dependencies
  • And many others

Although the version reduces many dependency risks, it is a pain to solve them from time to time.

Semantic Versioning

Almost every application in the world applies semantic Versioning, or at least a kind of semantic Versioning that makes sense in their scope. The “Semantic Versioning Specification” (SemVer) is quite simple and without many complex “bullet points”, but what is essential is the focus of the semantic of the Versioning, and not being semantic about the semantic.

Long story short, you have a component with the version composed in the following manner:

MAJOR.MINOR.PATCH

Where:

  • MajorMinor, and Patch are digits.
  • Change in the Patch number: implies that only bugs were fixed and are backward compatible.
  • Change in the Minor number: implies that features were added and are still backward compatible.
  • Change in the Major number: assume that breaking changes were made and are not backward compatible.
  • Every time a version changes one of these numbers changed, and all version changes are incremental.

For example:

  • If version 1.0.0 has a bug fixed (or more), then the version increases to 1.0.1.
  • If version 1.0.1 has some feature added, then the version increases to 1.1.0.
  • The Minor version increases, and the Patch version is to zero.
  • If version 1.1.0 has some feature added that introduced breaking changes, then the version increases to 2.0.0. The Major version increases, while the Minor and Patch version reset to zero.

While the Versioning of a public “API” starts in version 1.0.0, while we are at the beginning of the component development, the major version must be zero (0.y.z). By adopting this naming convention, the consumer knows that the component’s version is not intended for public use yet, so it may not be considered stable.

Semantic Versioning also provides a way to identify Production versions from Beta versions. To differentiate these versions, Beta versions have appended a hyphen and a series of dot-separated identifiers.

For example:

  • 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0–0.3.7, 1.0.0-x.7.z.92, etc.

And the pre-released version has lower precedence than the standard version, ex: 1.0.0-alpha < 1.0.0.

With this pre-released nomenclature, some may argue that it could be chaotic to identify the most recent pre-released versions. Which is most recent: 1.0.0-alpha or 1.0.0-alpha.1? Semantic Versioning also gives some recommendations to these scenarios.

Within two pre-released versions, we identify the most recent version by alphabetically sorting them.

From section 11 (Precedence refers to how versions are compared to each other when ordered), paragraph 4:

Precedence for two pre-released versions with the same major, minor, and patch version, MUST be compared each dot-separated identifier from left to right until identifying the difference, as follows:

  • Identifiers consisting of only digits are compared numerically in ASCII sort order.
  • Identifiers with letters or hyphens are compared lexically in ASCII sort order.
  • Numeric identifiers always have lower precedence than non-numeric identifiers.
  • A more extensive set of pre-released fields has higher precedence than a smaller set if all the preceding identifiers are equal.

Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

It is no coincidence that companies have released candidates’ versions with RC identifiers. For example, in Microsoft .NET Core there are the following versions:

  • 3.0.100-preview9–014004 < 3.0.100-rc1–014190 < 3.0.100

In the section “Backus–Naur Form Grammar for Valid SemVer Versions,” we have listed all valid combinations to describe a version semantically. But let us keep it simple.

If you like to verify if you’re working in a semantic version, SemVer has two regexes to validate the version. One of them you may check at this link: https://regex101.com/r/vkijKf/1/

Conclusion

Semantic Versioning is an essential concept in software development, and many tools rely on it to validate, control, and manage software and its dependencies. By using the concept with the proper semantic, developers can rest while consolidating the project’s dependencies and keep up to date with the dependencies versions.

It is vital that software development teams understand Semantic Versioning’s meaning and do not create ambiguity or semantics about them. Misconceptions create noise, and that noise may create integration challenges.

Nuno Cancelo
Polarising’s Microsoft Practice Lead

Author Profile

Microsoft Practice Lead | Senior Software Architect/Engineer | Community Leader | Developer Advocate