Breaking the code (without touching)

I managed to break one of my codebase (the master branch) without even touching it.

If you think about, the “issue” I ran into is trivial. It really is. Though, I want to write about it because often, these things get too little attention in my opinion.

So what did I do?

I maintain this project task-hookrs. The project itself depends on a few other crates. I updated these dependencies and then released a new version of my crate.

The rewrite included some lines which I had to change, not a big deal actually. One of my dependencies, namingly serde had a breaking change from 0.7.* to 0.8.0 which resulted in a small rewrite of a portion of my code. No functionality was changed, though.

I released 0.2.2 after the dependencies were upgraded. The last version before that version was 0.2.1. And despite me not changing any functionality, the micro-release was a mistake. Not because API changes (which I didn’t have) but because of link-time dependencies.

My other project, imag dependes on task-hookrs, but the dependency specification states task-hookrs = "0.2" (note the missing .1 at the end). When the master of imag got rebuild, it failed because imag itself depends on uuid = "0.2", whereas task-hookrs now depended on uuid = "0.3" - the linker failed because the types I passed from imag to task-hookrs were different.

Fuck.

What did I do? Well, I updated uuid in imag as well, everything solved. But that really got me thinking. What I should have done: Updating the minor version (0.2.1 -> 0.3.0).

We talk a lot about breaking changes in APIs and such, in fact this is why we introduced semantic versioning in the Rust community. What we do not talk about is that dependencies are also relevant when updating. Even the semantic versioning website states:

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

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards-compatible manner, and
  3. PATCH version when you make backwards-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

Which clearly states that MAJOR version when you make incompatible API changes - though nobody tells you that updating a dependency of your library might also be a incompatible API change!


After all I’m (kinda) safe here, because the semantic versioning spec also contains the following:

Major version zero (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.

So nobody can blame me. Although this really bugs me and it really shouldn’t have happened.