Revision of version numbers

Coordinator
Mar 20, 2014 at 4:10 PM
So, while cleaning up I Thought that our versioning system didn't have a system at all. It is more like: "Hey I feel I should increase it this way instead of that way".

I want to change that, badly, so I read up upon the Version class in the .NET framework and found their system is probably the way we want to go.

Here is the part I'm interested in:
Version numbers consist of two to four components: major, minor, build, and revision. The major and minor components are required; the build and revision components are optional, but the build component is required if the revision component is defined. All defined components must be integers greater than or equal to 0. The format of the version number is as follows (optional components are shown in square brackets ([ and ]):
major.minor[.build[.revision]]
The components are used by convention as follows:
Major: Assemblies with the same name but different major versions are not interchangeable. A higher version number might indicate a major rewrite of a product where backward compatibility cannot be assumed.
Minor: If the name and major version number on two assemblies are the same, but the minor version number is different, this indicates significant enhancement with the intention of backward compatibility. This higher minor version number might indicate a point release of a product or a fully backward-compatible new version of a product.
Build: A difference in build number represents a recompilation of the same source. Different build numbers might be used when the processor, platform, or compiler changes.
Revision: Assemblies with the same name, major, and minor version numbers but different revisions are intended to be fully interchangeable. A higher revision number might be used in a build that fixes a security hole in a previously released assembly.
Subsequent versions of an assembly that differ only by build or revision numbers are considered to be Hotfix updates of the prior version.
Starting with .NET Framework 2.0, the MajorRevision and MinorRevision properties enable you to identify a temporary version of your application that, for example, corrects a problem until you can release a permanent solution.
And from the Minor/Major Revision article:
Suppose you release an interim version of your application to temporarily correct a problem until you can release a permanent solution. The temporary version does not warrant a new revision number, but it does need to be identified as a different version. In this case, encode the identification information in the high and low 16-bit portions of the 32-bit revision number. Use the Revision property to obtain the entire revision number, use the MajorRevision property to obtain the high 16 bits, and use the MinorRevision property to obtain the low 16 bits.
However I have a slight problem with this system (which is apparently not so uncommon, as pointed out on this SO article)
I'd like to think of the version system in the same way as Charlie Hills thinks of it. Therefore I propose the following:

We use the standard versioning system as describe in the Version class article on MSDN. As we probably are not going to change anything which would affect the build number, we just leave it at zero.


Thought?
Developer
Mar 20, 2014 at 8:30 PM
I don't think the actual numbers you put in really matter to anyone, as long as they are incremental, because that's what people expect version numbers to be.

No matter what though, make sure you play by the rules of the framework. I know that you have to be really smart about changing the AssemblyVersion attribute in order to ensure that interchangeable builds of the library remain... well... interchangeable. And vice versa.

One other thing I want to comment on: currently, we encode API version information in the namespace. Is this really the best we can do? Perhaps we should make a separate branch for each version or something? Do we even want to make the library backwards-compatible when V2 is released (if ever)?
Coordinator
Mar 21, 2014 at 3:49 PM
I don't think the actual numbers you put in really matter to anyone, as long as they are incremental
You are right on this one. Still I want to put a system behind it, if possible. I think we should start with it when we release version 1.0. We will skip 0.9, because I see 0.9 as the current HTTP branch while 1.0 is the current HTTP branch with a caching system included.
After that we do the following:
Major.Minor.Build.Revision, just like the Version class, while
  • Major: A big release that is not assumed to be backwards compatible, e.g. we write new methods, classes, etc. and mark others obsolete.
  • Minor: Big releases where we guarantee backwards compatibility, meaning we rewrite a bigger portion of the code, but the end user doesn't have to change any code on his side to guarantee the code works as before.
  • Build: Different Framework, different processor architecture, etc. Will probably stay 0 all the time.
  • Revision: Minor bug fixes, where the major revision indicates temporary fixes and a full revision permanent fixes.
currently, we encode API version information in the namespace. Is this really the best we can do?
I'd like to keep it that way (for now). Since we don't know how ANet will release their OAuth api (which they are still working on apparently) and which will go into a V2. For example if their version 2 of the api will have every endpoint rewritten we could clearly separate the V1 and V2 part. However I'm open up to namespace usage suggestion and if we could improve on that part.
Developer
Nov 19, 2014 at 3:14 PM
Edited Nov 19, 2014 at 3:22 PM
Coming back to the versioning topic, let's also discuss a branching strategy.

I suggest 2 permanent branches, based on research that I did before writing this
  1. Main
  2. Release
Main is where most of the development happens, and always contains the latest revision of the code. We assume that Main is always in a stable state, even though we cannot ensure it due to CodePlex limitations. Rule of thumb: do not check in low quality code, but shelve it for later.

When Main has reached a state that is ready for release, it is merged into Release, at which point the code gets versioned and labeled. Then, Release gets merged back into Main so that development can continue on Main.

Important: any bugs found after release are hotfixed on Release and then merged into Main. The reason is that Main is intended for new developments, while hotfixes only apply to existing code. If Main already has new code when the bug is discovered, it would be very difficult to merge bugfixes from Main into Release without also merging unfinished code.

Last but not least, you are free to create an unlimited number of temporary branches (also called topic branches), as long as you can think of a proper name for them. When a topic branch has reached a state that is ready for release, it is merged into Main and then deleted.
Coordinator
Nov 25, 2014 at 10:39 AM
Sorry i didn't answer for so long. Math for my computer science studies kept me busy for more than a week (urgh... why math...).

I can only second that versioning and brancing model. We should go with it. Starting with the next version.
Developer
May 16, 2015 at 3:26 PM
Edited May 16, 2015 at 3:31 PM
I came up with a few rules that I use to version my other projects. Take away from this what you like.
 
  • Format of the version components: major.minor.build.revision
    • major: increment by 1 when a breaking change is introduced
    • minor: increment by 1 when an incremental change is introduced
    • build: automatically set by the build server when using continuous integration/nightly builds
    • revision: automatically set to the TFS changeset number by the build server.
  • Every project starts with major version 1, version 0 does not exist.
    • This is the default that Visual Studio sets for new projects
    • Pre-release code can instead be tagged with semantic version labels (e.g. 1.0.0-alpha).
    • Info: http://semver.org/
  • Don't be scared to increase the major version component.
    • Be scared of making breaking changes instead.
  • All projects in a repository have the same version
    • When a project needs its own version, it's probably a side-project that should move to a new repository
  • Increasing a version component does not mean that you have to reset the other components to 0.
    • Example: initial version: 1.2
    • Rename a public method
    • New version: 2.2 instead of 2.0
  • Each version component can be a number between 0 and UInt16.MaxValue
    • Except the major version component, which starts at 1
    • Minimum version: 1.0.0.0
    • Maximum version: 65535.65535.65535.65535
    • This rule exists so that versions can be parsed as UInt64 (ulong)
Coordinator
May 24, 2015 at 2:13 PM
Increasing a version component does not mean that you have to reset the other components to 0.
Care to explain this rule?
Developer
May 24, 2015 at 2:38 PM
Edited May 24, 2015 at 2:45 PM
The reason is that you shouldn't decrease the value of any version component.

Example (the wrong way):
old version is 1.2.3.4
new version is 2.0.0.0

At first glance, this looks like the proper thing to do, because 2.0.0.0 is the next major version greater than 1.2.3.4. However, there are at least two problems that I can think of:
  1. The build number changed from 3 to 0. Does this mean that the new version was built BEFORE the old version?
  2. The revision changed from 4 to 0. Did the new version get checked into source control BEFORE the old version?
The right way:
old version is 1.9.9.9
new version is 2.9.10.10

How do I come up with that new number? It's an action-reaction kind of rule.

(Assume that each commit results in an automatic build. Otherwise this won't make much sense.)

Action: user commits a changeset that contains breaking changes
Reaction: increment the major version by 1

Action: user commits a changeset that contains only non-breaking changes
Reaction: increment the minor version by 1

Action: user commits a changeset
Reaction: set the revision number to the changeset number
Exception: always use 0 for distributed version control systems like Git, because they use hashes instead of numbers.

Action: successful build
Reaction: increment the build number by 1

There is not a single action that results in decrementing a version component.
Developer
May 24, 2015 at 2:42 PM
Unfortunately, computers are not intelligent enough to know when a changeset is breaking or non-breaking, so the major and minor version components have to be provided by the user who commits the changes.
Coordinator
May 24, 2015 at 4:57 PM
At first glance, this looks like the proper thing to do, because 2.0.0.0 is the next major version greater than 1.2.3.4. However, there are at least two problems that I can think of:
I don't see those two points as problems at all. We can assume (and I agree on your action/reaction points), that 2.0.0.0 comes after 1.2.3.4, since the major version is higher for the first. Therefore the answers to your two points are: 'No' and 'No'

In C# the Version.CompareTo() does a comparison this way:
if (this._Major != v._Major)
    if (this._Major > v._Major)
        return 1;
    else
        return -1;
 
if (this._Minor != v._Minor)
    if (this._Minor > v._Minor)
        return 1;
    else
        return -1;
 
if (this._Build != v._Build)
    if (this._Build > v._Build)
        return 1;
    else
    return -1;
 
if (this._Revision != v._Revision)
    if (this._Revision > v._Revision)
        return 1;
    else
        return -1;
 
return 0;
This gives us consistency across the board. Assume we use the system you described, with the premise that we use a distributed version system. Therefore version 2.3.0.99 is higher than version 2.2.0.180, since the first has a higher minor version.

We reduce version components, but we don't get into conflicts, since we have a "order of precedence" for determining if a version is greater than another.
Developer
May 24, 2015 at 5:11 PM
Edited May 24, 2015 at 5:22 PM
You didn't address the real problem, which is that you can't do this:
var leftVersion = Version.Parse("1.2.3.4");
var rightVersion = Version.Parse("2.0.0.0");

if (leftVersion.Build < rightVersion.Build)
{
    // the version on the left was built BEFORE the version on the right... or was it?
}
else
{
    // the version on the left was built AFTER the version on the right... or was it?
}
This flow is incorrect if you reset the version numbers.

I feel like this is not important for the minor version number, but very important for the build and revision version numbers.

Also, you can't do this
var v1 = Version.Parse("1.2.4.5");
var v2 = Version.Parse("2.0.0.0");

if (v1 < v2)
{
    // v1 was built BEFORE v2... or was it?
}
That doesn't work either: you might have to hotfix v1 long after you released v2. So you can't assume that v2 was built after v1.
Coordinator
May 24, 2015 at 5:52 PM
Edited May 24, 2015 at 6:19 PM
That doesn't work either: you might have to hotfix v1 long after you released v2. So you can't assume that v2 was built after v1.
Ah, yes. That problem did slip my mind.
Developer
May 24, 2015 at 6:01 PM
Edited May 24, 2015 at 6:02 PM
I do feel like it's okay to reset the minor version number when you increment the major version number. I can't think of any scenario that would break. But I'd prefer if we didn't. It's not like we'll ever run out of numbers. :)
Coordinator
May 24, 2015 at 6:21 PM
Do we reset the build number, when we release a new version? I feel we should do it, after all the build is for a specific version and not overall.
Developer
May 24, 2015 at 6:26 PM
No; it IS actually an overall build number. Build automation software doesn't care about the major/minor version. Only the number of builds that it has created. So it never resets.