A Guide on Binary Compatibility - need your input!

I would be happy if each project would just explain in some standard way what its approach to source and binary compatibility is. But it would certainly be nice to have a way to specify so sbt won’t complain.

Another thing I wanted to mention is that I’ve done something like this a few times:

// old method
private[mypackage] def foo(bar: Bar): Unit = newFoo(bar, defaultBaz)

// new method
def foo(bar: Bar, baz: Baz): Unit = ???

This is another example of a binary-compatible change that’s not source compatible. By using package private, anyone recompiling has to use the new method, but the old method is still available without polluting the public scala API. I think this is a nice strategy for evolving APIs in a binary compatible way.


Updated OP.

@gmethvin That’s super useful to know! I’m assuming that this works because package private is a scala-only concept so the running bytecode can still access the method.

Still looking for a few more examples of unexpected sources binary incompatibility & tips on library design so keep them coming :slight_smile:

Yes. This has already been discussed but not acted upon: https://github.com/sbt/sbt/issues/2699

Hum, could you point me to the specific comment (or PR) in that discussion that addresses this problem? It’s a pretty long thread, so I may have missed something, but all I see is improved presentation of the eviction warnings that were there before. They’re easier to understand, but they’re still annoying when they’re inaccurate (possibly even more so now, since you’re now drowned in a wall of warning text while previously you had some relatively discrete lines). I don’t see a way for libraries to tell sbt that they follow semver for binary compatibility, and that sbt should not report a warning at all when it evicts Akka 2.5.4 in favor of 2.6.8.

Yes, here’s an extract:

More complete solution might be: Allow modules to declare version semantics.

Later down the thread I say this is the right way to do it, which is exactly what Guillaume propoaed here.

I have a first draft up in this PR. Please help me review and suggest improvements!

1 Like

Many thanks, @jatcwang. This sort of thing is immensely helpful to a lot of people. Thanks for undertaking this, really!

@sjrd, @ktoso, @jvican (and anyone else who has weighed in… maybe @smarter?) would you guys have a run through of @jatcwang’s proposed guide? You’re the experts here. Your input on this guide would be super valuable!

(@ktoso I saw you already made some comments on the PR, thank you! If you have anything else you’d like to add, please don’t hold back :slight_smile:)

1 Like

I just did a pass through the document, thank you again @jatcwang for your work. It feels really good that the Community helps us to convey such important “teachings” to the Scala devs.

I think it would make sense to have such mechanism. We do have the precedence of API docs attribute. Here’s my proposal for abiVersion setting - https://github.com/sbt/sbt/issues/3562

We need to be a bit careful about “SemVer” though. If sbt assumed SemVer 2.0 for all libraries, then it will warn that X 0.6.1 and X 0.6.2 are not binary compatible, since it does not cover 0.y.z.

Also for lower level libraries, I think we should also consider Persistent Versioning (http://eed3si9n.com/persistent-versioning) so 1.x and 2.x can live side by side.

Potentially relevant is Java’s binary compatibility guide/reference. I suspect this level of documentation might be unattainable (Sun had technical writers), but all the info in here is relevant.


Regarding inlining, Lukas Rytz quoted Scalac 2.12 release notes (https://github.com/scala/scala/pull/6088#issuecomment-330532820):

When inlining code from the classpath, you must ensure that all dependencies have exactly the same versions at compile time and run time.
If you are building a library to publish on Maven Central, you should not inline code from dependencies. Users of your library might have different versions of those dependencies on the classpath, which breaks binary compatibility.

From same thread:

When the inliner is enabled, the default heuristics may (and does) pick methods for inlining that don’t have the @inline annotation. This means that you cannot control the ABI with the optimizer enabled (except for -opt-inline-from).

Guidelines on how to use -opt-inline-from would help. I guess you can at least inline from the same library, but that might make more code affect the ABI.

EDIT: forgot to say, thanks @jatcwang for working on this!


Hello everyone, @jatcwang has done lots of progress on the binary compatibility guide front and I’d like to summon other experts to complement my reviews to his document. I feel this is a topic where we need a sweeping consensus (after all, his proposed scheme will be recommended in the official Scala website).

So if you want to see a growing Scala community that is more conscious of binary and source incompatibilities and uses a versioning scheme that forces us to reason about it, check his guide out: https://github.com/scala/docs.scala-lang/pull/881#pullrequestreview-72713752.