How much effort does backwards compatibility require from library authors?

From people i follow on Github i noticed a few pull requests that introduce support for the GHC version 9.12 but drop support for older (but not that old) GHC versions like 9.6, e.g. Add support for GHC 9.12, drop support for GHC 9.6 by tfausak · Pull Request #118 · tfausak/witch · GitHub and Drop support for GHC < 9.8 by brandonchinn178 · Pull Request #36 · brandonchinn178/toml-reader · GitHub.

Without any blame i’m curious why that is, because 9.6 seems to be an actively maintained version yet (see GHC versions: downloading and status · Wiki · Glasgow Haskell Compiler / GHC · GitLab) and for official Nixpkgs users like me 9.6 is still the latest version (until haskellPackages: Stackage LTS 22 -> 23; ghc: 9.6.6 -> 9.8.4 by sternenseemann · Pull Request #371032 · NixOS/nixpkgs · GitHub gets merged).

Is maybe the maintenance churn of keeping backwards-compatibility with GHC (doesn’t seem so in these specific PRs) or third-party tools like Github actions high? I’m asking, because in order to make Haskell more appealing to commercial companies migration paths should be as simple as possible and low-risk, i.e. if GHC can’t be backwards-compatible sometimes, then maybe the libraries can be, so that users don’t have to upgrade both at the same time. I think @angerman emphasized that a few times already, but i can’t find the post right now.

2 Likes

I agree dropping support for 9.6 is a bit aggressive for 2025. That is odd to me for such general libraries.

Two of your linked PRs involve changes to template-haskell bounds, and making libraries compatible with multiple versions of template-haskell can be tricky sometimes. I can understand the desire to limit the range of GHC versions to keep things simple.

But this is the first time I’ve seen someone call out aggressive dropping of support for old GHCs, and it’s a little surprising to me - the rule of thumb we use for work FOSS packages is:

  • At least the three most recent stable GHC releases, plus
  • Whatever GHC series we’re using internally (currently, we’re on GHC 9.6), plus
  • Any older GHC versions that happen to still work (but we’ll raise the base lower bound, not reach for CPP, if they stop working; that said, it’s often been fine and some of our simpler packages still support GHC 8.10.7)

It probably bears repeating: work is still on GHC 9.6. This is primarily because amazonka hasn’t had a release that supports GHC >= 9.8 (it’s on main, and I’m sorry it hasn’t progressed beyond that).

I think part of the reason it feels weird here is that both GHC 9.6.1 and 9.8.1 came out in 2023 (if the Discourse posts’ dates are correct), and because GHC 9.12.1 is still “not recommended for use” in the GHC downloading and status page linked by the OP. Given that GHC 9.12.1 has an issue that causes incorrect runtime behaviour, it’s probably correct that it’s been marked “not recommended for use”.

IMHO, a healthy starting point for most libraries (that aren’t locked to GHC internals for some reason) would be to support the “suitable for use” major releases, plus any newer GHC series as they mature. Maintaining support for older GHCs is a nice favour, but I don’t expect the average library maintainer to do that forever. (MicroHS is another potential reason to use simpler Haskell in libraries than one would in applications.)

For library consumers, I generally think that “if upgrading is too hard, it’s probably not happening frequently enough”. Work’s codebase is relatively small compared to others I’ve heard about (~120kLoC Haskell) and keeping it up-to-date is not too bad. I’ve also heard about larger codebases where a GHC upgrade is a year-long project. It seems better to me that upgrading is done frequently and with little friction than to have to worry about big-bang GHC-and-library upgrades (though we don’t want to go as fast as the JS ecosystem, of course). Otherwise, nobody gets to actually use the new things that people have been tirelessly working on. The biggest example of this is something like Backpack, which AFAICT nobody uses because Stack doesn’t support it, and Stack doesn’t support it because nobody uses it.

5 Likes

Why would a maintainer commit to do additional work for older GHCs? Previous releases are not going anywhere, you can keep using them with GHC 9.6.

5 Likes

I have limited bandwidth to maintain my packages. Supporting more versions of GHC (or any dependency!) is often easy, but it’s never free. My policy is to support the latest three major versions of GHC.

3 Likes

This seems reasonable and common, but I wonder if supporting the three most recent “suitable for use” major releases (per the wiki page in OP) would be a better standard?

1 Like

It sounds to me like the ideal solution would be commercial users funding maintenance for the configuration they are using. Sounds like that wouldn’t be too much of a time sink, while it’s a lot to ask of maintainers to gift companies this kind of busywork in their free time.

8 Likes

I’m curious whether there are specific issues making packages work with 9.12 which prevented keeping the lower bound where it was. If so then I will add them to my 9.12 breakage inventory.

3 Likes

This is a good point. I think the GHC-9.12 release of TH should’ve been easy to upgrade to, but I’d be interested in hearing if not.

I didn’t know that page existed before yesterday. The “suitable for use” distinction is new to me. However it’s not the first time an X.Y.1 release has had bugs. I’d prefer to support the latest release even if it’s not suitable so that people who aren’t affected by the bug can upgrade.

1 Like

You can see all my PRs for upgrading to GHC 9.12 here:

None of them required any code changes.

If they don’t require code changes, why drop support for 9.6? Doesn’t that mean that they do build with 9.6?

5 Likes

Why stop there? My Flow library hasn’t really had any code changes since it was released in 2015 (then, now). Should I still be supporting GHC 8.0? I’ll bet Flow compiles with even older versions of GHC as well — perhaps I should add support for everything back to GHC 6.6, around when Hackage showed up.

Less glibly, any support policy I make is going to exclude some people with totally valid use cases. To those people, I’ll echo what Andrew said earlier in the thread: Use an older version of my libraries to go with the older versions of GHC. Or, if that’s not palatable, use something like Cabal’s --allow-older flag. Or if even that doesn’t work, I’ll echo what Sebastian said: Pay me and I’ll (probably) support whatever you need.

2 Likes

What cost is there to not bumping the PVP lower bound when it’s observably unnecessary (like this case)?

I get not wanting to maintain a gigantic matrix of GHC versions in your library’s CI. That’s nontrivial. But that’s what tested-with is for. I also get needing to maintain a bunch of CPP or w/e to maintain compat. But you can just not do all that while not bumping the lower bound. It’s not a signal of future maintenance. Just that the current version works.

Worst case, you’re wrong and a user on an old GHC lets you know and you bump the lower bound then. Decentralized CI :laughing:

3 Likes

Why is it up to me to support the versions of GHC that you want? This is how I’ve chosen to maintain my packages. I have my reasons, but it sounds like they don’t resonate with you. That’s fine.

2 Likes

I’d like to comment from a different user perspective:

Thank you library maintainers, for giving me the option to use a recent GHC in a timely manner! The situation is so much better than a few years ago.

I don’t care about have an issue with using slightly older packages or trying to allow newer versions with an older version of GHC, as long as there is a working combination.

Edit: clarify

3 Likes

Naturally, you’re welcome to maintain your packages however you want for any reason or no reason at all. Sorry if you’ve already shared and I’ve either missed it or misunderstood, but I don’t think I know what your reasons are. Would you mind elaborating?

In the case of witch, for example, it looks like it would actually have been easier to keep support for 9.4 than drop it, as that would have required fewer changes to cabal and CI files. But maybe the reason is something else? For example, the simplicity that comes from knowing all the packages you maintain support exactly the same version range. I don’t want to put words into your mouth though: I’m just guessing here.

8 Likes

For me it’s not about any particular version of GHC. Sometimes it would not require much to maintain support, other times it would require a lot. I would prefer to have a policy that is easy to reason about (“latest three major versions”) rather than one that might have more supported configurations somewhat randomly (“whatever is easy to support”).

For Witch and GHC 9.6 specifically, I was about to drop something relatively minor from the package description:

  if impl(ghc >=9.8)
     ghc-options: -Wno-missing-role-annotations

I will take some time to write up a longer post with my actual rationale for my maintenaince policy. In the meantime, I will continue to push back against the implied assertion that I should continue to support GHC because it’s easy. Is anyone actually impacted by this? All of my packages have many released versions that work with GHC 9.6.

3 Likes

I keep a stable API for my packages, but I don’t give any thought to backwards compatibility wrt. GHC. Forwards compatibility tends to come for free by users of the package who are excited about the newest GHC out in the form of a PR. Amount of work for me is close to zero.

My assumption is that, like myself, people use Stackage or Nix’s haskellPackages (derived from Stackage), and use the version of a given package that’s appropriate for their version of GHC.

Thanks for the responses.

So far the discussion mostly evolved around “latest three major versions” policy. I’m very interested to read the rationale for it at some point, because my speculation would be that it’s a valid and pragmatic choice to reduce maintenance overhead (code, ci, discussions etc.) but could be alleviated with a larger Haskell user base to distribute the work. The same goes for a no-explicit-backwards-compatibility.

1 Like