Package compatibility with language versions

I’m looking for opinions about what is the best practice in dealing with differences in Julia language versions, e.g. between v1.0 and v1.4 with packages. Should packages stick with v1.0 compatibility as much as possible? Or should they go straight for new language features, even if it may break existing long-term-support?

The context is that I am considering submitting a pull request for NumericalIntegration.jl to make it compatible with OffsetArrays.jl. My first approach is to use begin:end style statements, and this makes the code easy to read. However, if this were included, it would break compatibility with earlier versions of Julia, and I don’t think the overlap of people who would use NumericalIntegration.jl with OffsetArrays.jl is that high. So it seems a question of purity vs practicality.

I realise my specific context is best asked on one of these package repos, so my question here is: what is the recommendation in general?

Is there another route that can be taken? E.g. include a separate branch, or a 3rd package to disambiguate this? Should the code be duplicated with version guards?

1 Like

The question is really about allocation of scarce resources, ie developer time and enthusiasm. Large projects with a lot of users may find the manpower and the motivation to deal with compatibility issues, but single-author packages might just choose to focus the same effort on adding new features, fixing bugs, etc.

If you need a new feature because it makes your life more convenient, just go for it, otherwise support the oldest version that works for you.

If you can, try to support the at least the last 2 minor releases, so currently 1.3 and 1.4 so your users have some time to upgrade.

2 Likes

My personal decision tree for such breaking changes is:

  • IF it makes the code nicer, but does not really imply a significant improvement in the package’s functionality THEN:
    • hold it and keep compatibility with older versions, until keeping that compatibility becomes a real burden.
  • ELSE IF it fixes a relevant bug THEN:
    • IF time available THEN:
      • invest some time to find out if there is a workaround compatible with the older version.
      • IF sound workaround is found THEN:
        • implement that solution and keep compatibility.
      • ELSE IF dirty workaround found, but much than the breaking change THEN:
        • consider implementing both, in a patch release, and a minor or major update of the package, respectively.
      • ELSE:
        • implement the breaking change at the expense of not supporting the LTS Julia version.
    • ELSE:
      • implement the breaking change at the expense of not supporting the LTS Julia version.
  • ELSE IF it introduces new features, improves performance, etc. THEN:
    • do it at the expense of not supporting the LTS Julia version.
  • ELSE:
    • (I don’t know, are there other options?)
2 Likes

Thanks for the thoughts! And the more-detailed-than-I-expected decision tree.

I think the specific case I have in mind falls into a grey area of these points. Although “… workaround compatible with the older version…” does apply here, so perhaps I can work with firstindex(arr) instead of begin and leave comments that this is recommended to be changed over in the future.

I do like the idea of going for new features over LTS support. I can imagine a backwards-compatible package could always be extracted out if absolutely needed for those cases.

2 Likes

I view LTS releases more as a feature for extremely specialized and niche usecases, like companies/government entities with lenghty security audits or projects that depend on specific julia internals.

The average user is much better off with the current release and there is a good chance that people who do have to use LTS releases don’t use packages from the public repository anyway, because their special requirements collide with executing arbitrary code, so they rather have curated custom versions of the things they need.

7 Likes

In your specific case, I’d probably just use the line that is backwards compatible. It isn’t going to be deprecated, it isn’t any slower…the difference is purely for readability.

In other cases where the differences are much more substantial, but the benefits far outweigh the cons, then I’d make the change and drop LTS support. You don’t need to do anything fancy here like different branch, code duplication, etc because the package manager can do that. You can just put a note in the README that version x.y.z is the last release that is compatible with LTS. Users can add that specific version of the package if they need. They then only miss out on the new features that you add, it isn’t like they can’t use the package at all.

2 Likes

While that is a nice courtesy, the Pkg resolver should be able to figure this out automatically for the user on each specific version of Julia.

2 Likes

Good points about the Pkg manager, I hadn’t thought of the automatic side of it. I guess that’s a strong argument for keeping the tests run across both the latest Julia version and also the earliest version you expect to be compatible. Soon as the earlier version starts to fail, you can bump the compat info in Package.toml (unless there’s a better alternative to keep compatibility).

1 Like