Version numbers in .NET Projects

Published by marco on

Any software product should have a version number. This article will answer the following questions about how Encodo works with them.

  • How do we choose a version number?
  • What parts does a version number have?
  • What do these parts mean?
  • How do different stakeholders interpret the number?
  • What conventions exist for choosing numbers?
  • Who chooses and sets these parts?

Stakeholders

In decreasing order of expected expertise,

  • Developers: Writes the software; may *change* version numbers
  • Testers: Tests the software; highly interested in version numbers that make sense
  • End users: Uses the software as a black box

The intended audience of this document is *developers*.

Definitions and Assumptions

  • Build servers, not developer desktops, produce artifacts
  • The source-control system is Git
  • The quino command-line tool is installed on all machines. This tool can *read* and *write* version numbers for any .NET solution, regardless of which of the many version-numbering methods a given solution actually uses.
  • A *software library* is a package or product that has a developer as an *end user*
  • A *breaking change* in a software library causes one of the following
    • a build error
    • an API to behave differently in a way that cannot be justified as a bug fix

Semantic versions

Encodo uses semantic versions. This scheme has a strict ordering that allows you to determine which version is “newer”. It indicates pre-releases (e.g. alphas, betas, rcs) with a “minus”, as shown below.

Version numbers come in two flavors:

  • Official releases: [Major].[Minor].[Patch].[Build]
  • Pre-releases: [Major].[Minor].[Patch]-[Label][Build]

See Microsoft’s NuGet Package Version Reference for more information.

Examples

  • 0.9.0-alpha34: A pre-release of 0.9.0
  • 0.9.0-beta48: A pre-release of 0.9.0
  • 0.9.0.67: An official release of 0.9.0
  • 1.0.0-rc512: A pre-release of 1.0.0
  • 1.0.0.523: An official release of 1.0.0

The numbers are strictly ordered. The first three *parts* indicate the “main” version. The final *part* counts strictly upward.

Parts

The following list describes each of the parts and explains what to expect when it changes.

Build

  • Identifies the build task that produced the artifact
  • Strictly increasing

Label

  • An arbitrary designation for the “type” of pre-release

Patch

  • Introduces bug fixes but no features or API changes
  • May introduce obsolete members
  • May *not* introduce breaking changes

This part is also known as “Maintenance” (see versioning”>Software versioning on Wikipedia).

Minor

  • Introduces new features that extend existing functionality
  • May include bug fixes
  • May cause minor breaking changes
  • May introduce obsolete members that cause compile errors
  • Workarounds must be documented in release notes or obsolete messages

Major

  • Introduces major new features
  • Introduces breaking changes that require considerable effort to integrate
  • Introduces a new data or protocol format that requires migration

Conventions

Uniqueness for official releases

There will only ever be one artifact of an official release corresponding to a given “main” version number.

That is, if 1.0.0.523 exists, then there will never be a 1.0.0.524. This is due the fact that the build number (e.g. 524) is purely for auditing.

For example, suppose your software uses a NuGet package with version 1.0.0.523. NuGet will not offer to upgrade to 1.0.0.524.

Pre-release Labels

There are no restrictions on the labels for pre-releases. However, it’s recommended to use one of the following:

  • alpha
  • beta
  • rc

Be aware that if you choose a different label, then it is ordered alphabetically relative to the other pre-releases.

For example, if you were to use the label pre-release to produce the version 0.9.0-prealpha21, then that version is considered to be higher than 0.0.0-alpha34. A tool like NuGet will not see the latter version as an upgrade.

Release branches

The name of a release branch should be the major version of that release. E.g. release/1 for version 1.x.x.x.

Pre-release branches

The name of a pre-release branch should be of the form feature/[label] where [label] is one of the labels recommended above. It’s also OK to use a personal branch to create a pre-release build, as in mvb/[label].

Setting the base version

A developer uses the quino tool to set the version.

For example, to set the version to 1.0.1, execute the following:

quino fix -v 1.0.1.0

The tool will have updated the version number in all relevant files.

Calculating final version

The build server calculates a release’s version number as follows,

  • major: Taken from solution
  • minor: Taken from solution
  • maintenance: Read from solution
  • label: Taken from the Git branch (see below for details)
  • build: Provided by the build server

Git Branches

The name of the Git branch determines which kind of release to produce.

  • If the name of the branch matches the glob **/release/*, then it’s an official release
  • Everything else is a pre-release

For example,

  • origin/release/1
  • origin/production/release/new
  • origin/release/
  • release/1
  • production/release/new
  • release/

The name of the branch doesn’t influence the version number since an official release doesn’t have a label.

Pre-release labels

The label is taken from the last part of the branch name.

For example,

  • origin/feature/beta yields beta
  • origin/feature/rc yields rc
  • origin/mvb/rc yields rc

The following algorithm ensures that the label can be part of a valid semantic version.

  • Remove invalid characters
  • Append an X after a trailing digit
  • Use X if the label is empty (or becomes empty after having removed invalid characters)

For example,

  • origin/feature/rc1 yields rc1X
  • origin/feature/linuxcompat yields linuxcompat
  • origin/feature/12 yields X

Examples

Assume that,

  • the version number in the solution is 0.9.0.0
  • the build counter on the build server is at 522

Then,

  • Deploying from branch origin/release/1 produces artifacts with version number 0.9.0.522
  • Deploying from branch origin/feature/rc produces artifacts with version number 0.9.0-rc522

Release Workflow

The following are very concise guides for how to produce artifacts.

Pre-release

  • Ensure you are on a non-release branch (e.g. feature/rc, master)
  • Verify or set the base version (e.g. quino fix -v 1.0.2.0
  • Push any changes to Git
  • Execute the “deploy” task against your branch on the build server

Release

  • Ensure you are on a release branch (e.g. release/1)
  • Verify or set the base version (e.g. quino fix -v 1.0.2.0`)
  • Push any changes to Git
  • Execute the “deploy” task against your branch on the build server