Versioning

Versioning helps us to create cornerstones in a project so that we can understand the project state. In this part versioning and semantic versioning will be discussed.

Semantic Versioning

Semantic versioning offers a scheme as below:

[MAJOR.MINOR.PATCH]

Depending on whether a change breaks backward compatiblity and the risk that comes with it, one or many of those three digit should be incremented. Speaking of backward compatiblity, semantic versioning assumes that every software interact with the world around it via a public API.

  • If a modification causes change or lost of existing behaviour, that modification is classified as non-backward compatible. Therefore MAJOR unit should be increased

  • If a modification does not break backward compatiblity and yet it adds new functionalities to pulic API, this should cause an increment in MINOR unit

  • Any modification that does not belong to first two bullets will end up with an increment in PATCH unit (e.g. bugfix)

The risk comparison between increment of units would be MAJOR > MINOR > PATCH. There is no hard and fast rules when it comes to incrementing, so a jump between version 1.0.1 to 2.0.0 is completely legit as long as it is not completely random and there is an explanation behind it.

Semantic Versioning in Action

In this part we will develop a hypothetical application gradually and use semantic versioning along the way. Let’s say you’re building an application and that is called network-manager. It has one initial requirement, it should return the IP address of network interface wherever it runs and that functionality should be available under /get-ip-address endpoint.


NOTE

There are several ways to communicate with the application. We can use arguments/options, listen for keyboard strokes, start a server inside and communicate via a client application. We’re not going into details but the idea is that an application one way or another presents interface to interact with the world around it and that part is called public API. It’s important to define which part of your software constitutes public API to use Semantic Versioning correctly.


After adding this functionality we can create a tag as v0.1.0 to point initial version.

If you run the application and reach for /get-ip-address endpoint, it responds as below:

{
    "interface": "enp0s3",
    "ip_address": "192.168.10.2/24"
}

After a certain amount of time you realized that it takes too much time to respond. You find out the reason and send a bugfix. Time to pump the version. This change was backward compatible because the application still responds the same way. Also we didn’t add new functionality. So no need to bump major or minor units, just increasing patch unit will be enough. So the new version is v0.1.1

It’s time to bring new functionalities to the table ! You realized that it would be a good idea to show MAC address and it should be reachable via /get-mac-address endpoint. After developing this functionality time to check if everything works fine.

So you run the application and ask for MAC address via /get-mac-address, here is the response:

{
    "interface": "enp0s3",
    "mac_address": "0a:0b:0c:0x:0y:0z"
}

It looks fine and we should give a new version. For now we have two different endpoints (/get-ip-address and /get-mac-address). Since we didn’t touch /get-ip-address endpoint, it still behaves as before and because we didn’t have any other than /get-ip-address, it’s okay to say this change was backward compatible. Yet this is a new feature, so we should bump minor unit. The new version is v0.2.1

Network-manager returns the first interface it reaches currently and because we only got one network interface in our workstation that was okay. But there might be more than one network interfaces where our application runs. The new goal is returning all IP and MAC addresses if applicable. After few changes, time to run basic checks in a different environment where we got two physical network interfaces:

/get-ip-address responds with:

[
    {
        "interface": "enp0s3",
        "ip_address": "192.168.10.2/24"
    },
    {
        "interface": "enp0s4",
        "ip_address": "192.168.11.2/24"
    } 
]

/get-mac-address responds with:

[
    {
        "interface": "enp0s3",
        "mac_address": "0a:0b:0c:0x:0y:0z"
    },
    {
        "interface": "enp0s4",
        "mac_address": "1a:1b:1c:1x:1y:1z"
    } 
]

Before this change endpoints were returning inside JSON objects, but now it returns JSON objects inside an array. This means consumer of the network-manager should change their code to be compatible with new version of network-manager and our change was not backward-compatible. We have to bump the major unit. The new version is v1.2.1