Language: EN

programacion-versionado-semantico

What is versioning in programming

Versioning is an essential practice in software development that allows us to track and control the changes made to a project over time.

The concept of version is something we use every day, and it is not exclusive to programming. For example, imagine you have a text document, and later you modify this document.

Instead of overwriting the same document, you save it as a new one. This way, you can have traceability of the modifications. Well… you just created a new version of your document.

programacion-versionado

Document versions

If you are more or less rigorous, you will set some rules to designate each version. For example, you put a v1, v2 at the end, or the date, or whatever. If not, you will end up like in the joke… ‘final_document’, ‘final_document_now_yes’ 😉.

Working with versions is always something complicated, and it requires rigor and care. In general, it requires you to have a series of rules or norms and to follow them strictly.

As I said, versioning exists in many fields. It exists in documents, it exists in manufacturing plans. Each sector has its own tools to manage versioning.

However, in software development, version control is even more important. This is because if something is modified, there is a very real possibility that your program will stop working.

What is versioning?

Technically speaking, versioning refers to the assignment of labels or numbers to the different modifications of the software as it is developed and updated.

Each version represents a specific state of the project at a given moment (this may include changes, improvements, or fixes compared to previous versions).

Version management is very relevant (essential?) for the reliability of software in large-scale projects. Both for:

  • People who use your development
  • Dependencies you use in your code

Managing versioning manually would be very hard. Fortunately, there are tools for version management, such as source control and package managers.

Semantic versioning

As I mentioned, an important part of versioning is deciding the rules and guidelines you can use to name the versions. There are many systems for designating versions, or you could even create your own.

However, there is a system called semantic versioning that is widely used in the development community. Although it is not the only one, it has become practically an accepted standard.

Semantic versioning consists of three numbers that identify the version:

MAJOR.MINOR.PATCH

Let’s see each of those parts and the rules that lead to incrementing them:

  • MAJOR: The major version number indicates significant changes that are incompatible with previous versions (generally, it is incremented when changes that may break compatibility with previous versions are introduced).

  • MINOR: The minor version number indicates new features or improvements that are backward compatible (by incrementing the minor number, it indicates that features have been added without affecting compatibility with previous versions).

  • PATCH: The patch version number indicates bug fixes or minor adjustments that do not affect backward compatibility.

Additionally, there is another rule. Each time a number is incremented, the ones “below” it reset to zero. That is:

  • If MAJOR changes, we reset MINOR and PATCH to zero
  • If MINOR changes, we reset PATCH to zero

Example of semantic versioning

It is probably easier, and if we look at an example. Suppose you have a program or a library, and you release an initial version v1.0.0

1.0.0 - Initial version

After some bugs arise, I fix them in a Bugfix. Since they are just bugs, I increment the PATCH field.

1.0.0 - Initial version
1.0.1 - Bugfix one
1.0.2 - Bugfix two

More time passes, and you want to add a new feature. This functionality is compatible with the previous version, so I increment the MINOR field. Additionally, by incrementing MINOR, I have to reset PATCH to zero.

1.1.0 - New feature

Of course, each new version can have its own PATCH, which would generally correspond to bug resolutions or very small changes.

1.1.0 - New feature
1.1.1 - Bugfix
1.1.2 - Bugfix

Now even more time passes, and you want to add more features. But this time, you are going to make important changes that break compatibility with the previous version.

In that case, I increment the MAJOR version, and both MINOR and PATCH reset to zero.

2.0.0 - Breaking change

What is breaking compatibility

I have said several times that the criterion for defining whether it is a MAJOR or MINOR version is whether it breaks compatibility. But what does “breaking compatibility” mean? Let’s look at it in more detail with an example.

Imagine you have created a library that generates QR codes. The syntax for generating the codes is very simple; you just pass the text you want inside the QR and the path where to save the generated QR.

// v1.0
generateQR('my text', 'path_image')

Now you add an optional parameter to change the color of the QR code. This is a change that does not break compatibility. Any code using your library will still work.

// v1.1 now there is an optional color parameter
generateQR('my text', 'path_image', 'pink')

// if you do not specify the color, it works just like v1.0. It does not break compatibility
generateQR('my text', 'path_image')

But, for example, at some point, your library supports so many options that passing them as parameters is not practical. So you decide to create a new object called options that is passed to the generateQR function.

// v2.0
options = { path: 'path_image', color: 'pink' };
		   
generateQR('my_text', options);

Anyone using your library v1.0 will not be able to use it. So it is a change that breaks compatibility and therefore the new version must be v2.0.

Partial compatibility break

I don’t want to be repetitive, but it is important to understand what breaking compatibility means. You break compatibility even if it does not occur in all cases.

Let’s forget the previous v2.0 for a moment and return to the case where you had v1.0.

// v1.0
generateQR('my text', 'path_image')

Imagine that (who knows why, it’s just an example) your new version of the code generator will not accept the character -. *(for example, because you use it internally for who knows why).

Whatever the case, from now on, if someone wants to use a - in your library, they will have to ‘escape’ it by putting \-.

// v1.0
generateQR('my-text', 'path_image')

// v2.0 special characters must be escaped
generateQR('my\-text', 'path_image', 'pink')

Most programs using your library, which did not use the - character at all, will work without problems.

But just the poor one who has had the bad luck to use a -, will have their program crash badly. So it is a MAJOR version change, even if it only breaks in certain cases.

In summary, in semantic versioning, what does it mean if you find that one of the fields is higher than the version you are using?

  • PATCH: You can update without problems; I just fixed bugs
  • MINOR: You can update without problems, although there are new features available
  • MAJOR: You should take a look at your program; in some cases, it may crash