[Article] Dear Google Cloud: Your Deprecation Policy is Killing You

That’s what all the concern-mongering in this thread reads like :man_shrugging:

Regarding multiple dispatch and deprecations, yes, for simple renames and changes to argument types, you can do deprecations pretty easily. But those are the least interesting kinds of changes for 2.0. There’s an issue on GitHub with a bunch of name changes, but you’ll note that core devs aren’t exactly flooding it with changes. Perhaps there are better names for some things, but that kind of name game doesn’t really interest me. We could certainly pick a few better names and put compatiblity shims in a backwards comat package or something like that but meh.

Keeping deprecations isn’t without cost. Even for the simple ones, all those extra names and methods take up compilation time and increase the size of method tables. There was a marked performance improvement when we ditched all the deprecation baggage going from 0.7 to 1.0. There are also really complex syntax and semantics deprecations that are much harder because they complicate the parser and/or the compiler. By 0.7 those had gotten really bad and made the parser and compiler much harder to work on that necessary. Deleting those was pure joy and allowed improvements that would have been really hard to do with the deprecations gumming up the works.

The truly interesting kinds of changes are precisely the ones that are really hard to deprecate. For example, if we want to introduce more immutable data structures and let the compiler take advantage of that, then APIs that currently return mutable data would change to return immutable data. That’s a breaking change: if someone was calling sin.(v) and then mutating the result, their code will break. But that’s fairly rare: usually people mutate arrays when constructing them and later when doing mathy stuff, work with them as values, i.e. applying functions to transform one immutable value to another. You can provide a shim that acts like 1.0 and “unfreezes” the returned arrays before returning it, but if everyone keeps using that, what’s the point? It seems much better to break this and tell people to fix their code by adding unfreeze calls where necessary. We can even do it automatically. But if all the code in the ecosystem keeps doing the 1.0 behavior, then there’s no benefit from the change.

Another example is that there has been some work done at Northeastern on the tractibility of Julia’s type system. It seems like contravariant type bounds make subtyping undecidable. But it also seems like we can limit the kinds of lower bounds that are allowed and eliminate the undecidability. In practice we suspect that hardly anyone ever uses type bounds in this problematic way. But changing this is technically breaking. It may turn out that relying on this is so rare that we can reasonably change it in a 1.x release, but what if that’s not the case? Then this would be a prime candidate for a 2.0 change: break some obscure usages of parametric types but in exchange guarantee that Julia’s subtyping algorithm will always terminate.

Another breaking change that we would like to pursue is enabling threading and parallelism by default. This is breaking because there’s a lot of code out there that is very thread-unsafe and will break if Julia changes to doing everything in parallel by default. It’s possible that we can coax the ecosystem into being sufficiently threadsafe to make this change in a 1.x version, but I’m a bit skeptical of that possiblity. We may have to change this in 2.0 and just tell everyone “If you want to use Julia 2.0, you need to make your stuff threadsafe.” Just pull the bandaid off quickly and then move forward into a brave new, parallel world.

I could go on, but hopefully you get the picture. Julia 2.0 is emphatically not about tedious renames.

44 Likes