> There is no "kinda incompatible", "99% compatible" - when it comes to dependencies, they either work properly or they don't.
> Without being strict about semantic versioning, it is impossible to make it so.
There is no semantic versioning if you're that strict. You have to update the major version on every single release, no matter the contents, if your idea of backwards compatibility is that absolute.
Let me give you a concrete example: I have a go package with this code:
var ErrBadThing = errors.New("somthing bad happened")
func DoThing() error { /* maybe return ErrBadThing */ }
I notice I had a typo in my error message, "somthing" when I wanted "something". I fix that typo. Is this a breaking change?
The answer is "it depends"
err := mypkg.DoThing()
if errors.Is(err, mypkg.ErrBadThing) { /* will still work after change */ }
if strings.Contains(err.Error(), "somthing") { /* will no longer work after change */ }
So, is it a breaking change or not? I would argue "no, not breaking, if the user uses the API as expected, it does not break", but you may argue otherwise.
If you argue that is a breaking change, well, what about adding a new method, which breaks reflection (like 'reflect.NumMethod()' returns one more, so if someone relied on indexing into your methods with reflection, you broke em!)? What about someone downstream applying a "patch" to your code before compiling it? Any change can break that.
The go authors have taken a much less strict approach. Go is still semantically "v1", but they've made a ton of breaking changes, from making `net/http` silently switch to http/2, to changing the semantics of various tls and security related functions, etc etc.