Dart & semver hygiene
What is semver?
The term semver means “semantic versioning” and is used to describe software releases.
It is defined here.
The essence of this rule-set is to couple the kind of (API) change with the version of the software. If you make a breaking change then you have to increase the major version and for non-breaking changes you have to increase the minor version (simplified).
This rule-set is the recommended way to version dart packages and all the mechanisms to resolve versions of dependencies rely on that.
What is the problem?
In general, I think semver works quite well, but sometimes it happens that a breaking change slips through.
This can have dramatic consequences.
If a package releases a new version and only increases the minor version or the patch version while still containing breaking changes then builds of dependent projects will fail without the developer specifying a new version.
This is because it is considered best practice (even a requirement) for packages to not depend on an exact version but on a compatible version range. This is done by assuming that any newer version that doesn’t increase the major version is backwards compatible.
But even if you don’t have a direct dependency on that package you might have a transitive dependency (e.g. if a package you depend on depends on that package) and you get your build broken as a result.
Is there something I can do about it?
Yes.
At least soon™️.
We were facing such a transitive dependency build breakage and I wondered if there is an easy way to integrate a semver check into a CI pipeline and why package maintainers don’t check that automatically.
After some research I was not able to find a utility that could be used to do such a check, so I decided to create one myself.
Welcome dart_apitool
I named the tool “dart_apitool” (I know. Such an inventive name 😉).
It can create a model of the public API of a given package and diff two public API models while checking the version regarding semver.
The package reference can be
- a model stored in a file (dart_apitool allows you to extract that)
- a copy of the old package on disk
- a pub-reference (dart_apitool will download the specified version of the package and extract the API model)
So the only thing a package maintainer has to do is to tweak the release process so that a reference is stored (can be just the released version number) which allows the CI pipeline to use that stored reference to check if the next version is still semver compliant.
I tried to cover all the different parts that make up a public API, but I’m sure I missed something. So please feel free to try it out and report any issues you have with it.
This is how a dart_apitool output can look like if it detects a problematic version:
Let’s improve semver hygiene of the dart package ecosystem!