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 in a project over time.

The concept of versioning is something we use every day, and it is not exclusive to programming. For example, imagine you have a text document, and then 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 have just created a new version of your document.

programacion-versionado

If you are somewhat 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… ‘documento_final’, ‘documento_final_ahora_si’ 😉

Working with versions is always complicated, and it requires rigor and care. Generally, it requires that you have a series of rules or norms and that you follow them to the letter.

As I said, versioning exists in many fields. It exists in documents, it exists in manufacturing blueprints. Each sector has its own tools for managing versions.

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 tags or numbers to different modifications of the software, as it is developed and updated.

Each version represents a specific state of the project at a given time. This usually includes changes, improvements, or corrections regarding previous versions.

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

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

Doing version management by hand would be very hard. Fortunately, there are version management tools, such as source code control and package managers.

Semantic versioning

As I said, an important part of versioning is deciding the norms and rules that you can use to name the versions. There are many systems to designate 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 these parts, and the rules that lead to their increment:

  • MAJOR: The major version number indicates significant and incompatible changes with previous versions. Generally, it increases when changes are introduced that may break compatibility with previous versions.

  • MINOR: The minor version number indicates new features or improvements that are backward-compatible. By increasing the minor number, it is indicated 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, those “below” it are reset to zero. In other words:

  • 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 to understand this with an example. Suppose you have a program or a library, and you release an initial version v1.0.0

1.0.0 - Initial version

Then some errors arise, which you fix in a Bugfix. As they are only bugs, you 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 functionality. These functionalities are compatible with the previous version, so you increment the MINOR field. Also, by increasing the MINOR, you have to reset PATCH to zero.

1.1.0 - New functionality

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

1.1.0 - New functionality
1.1.1 - Bugfix
1.1.2 - Bugfix

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

In that case, you increment the MAJOR version, and both MINOR and PATCH return to zero.

2.0.0 - Change that breaks compatibility

What is breaking compatibility?

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

Imagine that you have made 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 continue to work.

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

// if you don't put the color, it works the same as v1.0. It does not break compatibility
generateQR('my text', 'path_image')

But, for example, at some point your library accepts so many options that passing them as parameters is not practical. So you take a leap and 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);

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

Partial compatibility break

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

Let’s forget for a moment the previous v2.0, and let’s go back to the case where you had v1.0.

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

Imagine that (for whatever reason, it is an example) your new version of the code generator will not accept the ’-’ character. For example, because you use it internally for (for whatever reason).

Anyway, from now on if someone wants to use a ’-’ in your library, they will have to ‘escape it’, putting ’-‘.

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

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

Most programs that use your library, which were not using the ’-’ for anything, will work without problems.

But precisely the poor guy who has had the bad luck to use a ’-’, his program will 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 have only 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 will crash