Friends of OpenJDK Today

(Semantic) Versioning your Java libraries

January 03, 2024

Author(s)

  • Avatar photo
    Jago de Vreede

    Jago is a full-stack software engineer at OpenValue, as a software engineer he has seen a broad-spectrum of projects and he has been working on multiple large scale educational software ... Learn more

There are a lot of ways to version your library but the semantic versioning scheme is the most used and for a good reason, by looking at the version change you can already defer if you can upgrade the dependency without any problems or if you might have to do some refactoring. Semantic versioning proposes a simple set of rules and requirements that dictate how version numbers are assigned and incremented.

Given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes
  • MINOR version when you add functionality in a backward compatible manner
  • PATCH version when you make backward compatible bug fixed

If you do semantic versioning by hand then mistakes could slip in. I can’t remember how many times I've updated a library with a patch version that would then break because the API had changed significantly. For this reason, I’ve created the semver-check maven plugin. This plugin can check if the version of your library is set correctly.

The Semantic Versioning Specification gives us generic guidelines on how Semantic Versioning must be done. Still, it needs to be tailored for a specific language so it leaves room for mistakes when translating to a specific language.

For example, changing the Java compiler output from 11 to 17 without changing any code is often mistaken for a patch version, as no code has been changed. However, this is a breaking change for those who still use your library and are still on Java 11, so therefore, this change must be handled as a major change.

Versioning

Major
When you make incompatible API changes, this is implemented with the following checks:

  • Changing the java version of the compiled classes to a higher version.
  • Removal of a public class, method, field or static variable.
  • Removal of a resource file
  • Removal of an annotation on a public API

Minor
When you add functionality in a backward compatible manner, this is implemented with the following checks:

  • Addition of a public class, method, annotation, field or static variable.
  • Removal of an annotation on a non public API

Patch
When you make backward compatible bug fixes, this is implemented with the following checks:

  • Any change that changes the byte code
  • Any change in a resource file (Note that files in META-INF/maven/ are ignored as they are generated by maven)
  • Any change in a dependency

Integrating

There are two ways of integrating the automatic version check in your current pipeline build.

  1. With commandline arguments for example:
    mvn package io.github.jagodevreede:semver-check-maven-plugin:check
  2. and/or add it to the pom.xml (and replace VERSION_NUMBER with the latest released version)
<build>
    ...
    <plugins>
        ...
        <plugin>
            <artifactId>semver-check-maven-plugin</artifactId>
            <groupId>io.github.jagodevreede</groupId>
            <version>VERSION_NUMBER</version>
            <configuration>
                <haltOnFailure>true</haltOnFailure>
                <outputFileName>nextVersion.txt</outputFileName>
            </configuration>
            <executions>
                <execution>
                    <id>check</id>
                    <phase>verify</phase>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
    ...
</build>

You need to run at least the package phase to be able to have valid results of the semver-check

The output of this run will be something like this:

[INFO] --- semver-check-maven-plugin:0.4.1:check (default-cli) @ some-module-core ---
Downloading from central: https://.../some-module/0.4.1/some-module-core-0.4.1.pom
Downloaded from central: https://.../some-module/0.4.1/some-module-core-0.4.1.pom (1.4 kB at 14 kB/s)
Downloading from central: https://.../some-module/0.4.1/some-module-core-0.4.1.jar
Downloaded from central: https://.../some-module/0.4.1/some-module-core-0.4.1.jar (16 kB at 156 kB/s)
[INFO] Looking up versions of org.acme.example:some-module-core
[INFO] Checking SemVer against last known version 0.4.1
[INFO] Class org.acme.example.MyClass has been changed on byte level
[INFO] Class org.acme.example.OtherClass has been changed on byte level
[INFO] File META-INF/MANIFEST.MF has been changed
[INFO] Determined SemVer type as patch and is currently none, next version should be: 0.4.2

The output gives us a summary of what the next version should be and a summary of why. Please note that the plugin stops searching for patch changes if it already detects a minor update for example. This keeps execution time as low as possible.

There are many configuration options available for the plugin check out the readme for all available options.

The plugin also writes a file in the target folder called nextVersion.txt by default. You can also make your pipeline read this file and set the next version to be released with for example the maven versions:set plugin

Both multimodule projects and single modules are supported. The plugin will suggest the highest version bump encountered in a project.

So try it out in your library and if you encounter any issues please raise them on github.

Topics:

Related Articles

View All

Author(s)

  • Avatar photo
    Jago de Vreede

    Jago is a full-stack software engineer at OpenValue, as a software engineer he has seen a broad-spectrum of projects and he has been working on multiple large scale educational software ... Learn more

Comments (4)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Java Weekly, Issue 524 | Baeldung

[…] >> (Semantic) Versioning your Java libraries [foojay.io] […]

Anas

Thanks Jago for the plugin and the post.
Excuse me for my ignorance, I assume that the plugin should compare the current state of the project/library with the previous version to determine whether the changes are major, minor, or patch.
Am I correct?
if yes, how could the plugin know the previous state? does it depends on the code version control system (i.e git)?

Avatar photo
Jago de Vreede

@anas You are correct the project to the previous version to determine whether the changes are major, minor, or patch.
The plugin downloads the precious state from the last version released to the maven repo. So no dependencies on version control.

Java Digest #9 - TechBurst Magazine

[…] (Semantic) Versioning your Java libraries. The semantic scheme of versioning Java libraries has long been a standard. It is simple and intuitive. But many may have come across the fact that the patch version allegedly contains backwards incompatible changes. The author has written a maven plugin that automatically checks the changes made and suggests which part of the version number needs to be updated. […]

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard