Juju has a lot of external dependencies that are under the juju project. When we moved away from
go dep to
go mod, one thing we really need to understand is that
go mod forces Semantic Versioning (semver for short) on to the library developer.
With this in mind, we have to be very aware of the changes we’re making and that we maybe impacting others that might also consume our libraries. The onus is on us to ensure that our API doesn’t change or break with in the same major version of the library.
There are a few gotchas that can be thought of as non-breaking changes, but are a breaking change…
A recent example I hit, was wrapping or promoting an error from a std error to a
juju/error is a breaking change and we should bump the major version of the library.
You may question why, as the new
juju/error conforms to the
error interface, but it’s not the same type.
Errors highlight this edge case more than other types, because of the way go makes you handle errors or at least checks for them. Sentinel (singleton) errors are currently the standard way to ensure that an error is of type
X. So if a piece of code is trying to check for instance if
os.IsNotExist(err) and previously it was fine, but with our wrapped error they now have to do
os.IsNotExist(errors.Cause(err)). That is a breaking change, as you’re forcing your consumer of the library to change their code.
Semver aims to prevent that, you should be allowed to move up the minor/patches without changing your code…
We need (I’m generally speaking as me here), need to be fully aware of this, and check when doing code reviews that wrapping an error in go when using
go mod is harmful.
There are probably more cases where we fall short, but we should ensure we do better.