Skip to content

Instantly share code, notes, and snippets.

@e2
Last active April 8, 2020 05:30
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save e2/ac32569852cbd31f7da637500174d907 to your computer and use it in GitHub Desktop.
Save e2/ac32569852cbd31f7da637500174d907 to your computer and use it in GitHub Desktop.
How to stop the "Backward Compatibility Cult" before it kills Ruby.

How to stop the "Backward Compatibility Cult" before it kills Ruby.

  1. It's irresponsible to support Ruby < 2.2. A Ruby version is not "supported" if it's only getting security fixes. Because "support" means fixing bugs. And any Ruby version < 2.2 is rife with "never-to-be-fixed" bugs that can occur at any time. A minor OS upgrade, a minor gem upgrade ... and suddenly a new (or old) bug is triggered and Ruby crashes every time. At this point, the user may be forced to upgrade (at the most inconvenient time possible). The only other option is for everyone to implement some obscure workaround to side-step the bug in their outdated Ruby version. Ridiculous effort and costs just to keep a likely vulnerable (and definitely broken - even if it "works") version of Ruby around.

  2. Ruby 2.2.4 fixes an important vulnerability. There is a patch-level version of Ruby 2.0 (patch-level in code terms, not SemVer) and a minor of Ruby 2.1.9 to solve that security issue. But it's huge strain on the community to support anything below Ruby 2.0. It's irresponsible to "promote" any "backward-compatiblity" to allow end users to keep vulnerable versions of Ruby in their apps.

  3. It's unreasonable to expect gems to upgrade seamlessly - and not expect the same for Ruby (where core devs work very hard to make sure bugs are fixed and improvements make life better without migration headaches). There's a huge amount of effort put in to assure migration is as easy as possible for something so complex as Ruby. Migrating from 1.9.2 to 2.3.1 is trivial compared to migrating from 1.8.7 to 1.9.2. In short, it's unreasonable for users to only upgrade gems not upgrade Ruby (because it's a dependency like any other). It's more fair to either "upgrade everything" or "upgrade nothing" or "ask for and financially support backporting". IMHO, backward compatibility increases the amount of work and stress tenfold. And there's no "reward" for backward compatibility. (Because end users usually don't know or don't care - they simply see a lack of new features as stagnation, so they move to other languages/platforms like Golang, Elixir, Node.js, etc.).

  4. Due to backward compatibility, 99.9999% of Ruby code out there is still 1.8.7 compatible. And if contributors have to wait 5-10 years before then can "safely" use Ruby 2.3 features, the community will die like the Java community. "Death from Backward-Compatibility".

  5. If someone doesn't want to upgrade Ruby, there's no point in making anything beyond a patchlevel (SemVer) backward compatible with a previous Ruby. This means you can drop Ruby < 2.2 support in a 1.1.0 release, even if your 1.0.0 release supports Ruby 1.8.7. There's no SemVer need for any "API deprecation", because that should be handled by Ruby. By analogy, expecting a single codebase to work on both Python 2.x and Python 3.x is irrational. It destroyed the adoption of Python. See https://www.youtube.com/watch?v=LE0g2TUsJ4U for details (2015 talk by Matz).

  6. Newer version of Bundler (1.12 - almost released) can now resolve to an earlier version of a gem to match the ruby requirement in a gemspec. E.g. if a gem 1.1.0 requires Ruby 2.2, then someone with Ruby 1.9.3 (no matter how detrimental that is) will get an earlier "1.9.3 compatible" version installed, e.g. 1.0.0. This is why dropping a major version of Ruby doesn't require a major bump in a gem. SemVer is about constraints, so it's up to a resolver tool (like Bundler) to automatically find versions within those constraints. SemVer doesn't say "upgrades MUST be installable". It only says that APIs should work once matching versions ARE ALREADY installed. SemVer doesn't cover installation, the "dependency declaration process" and actual building. This means it's not your gem's responsibility to support multiple versions of Ruby. I can, but that's mostly useless (to end users), pointless (given upcoming Bundler version) and if not extremely harmful (to the Ruby community). If you're in doubt, just consider Ruby a gem that you can't "automatically upgrade". And so, consider supporting multiple versions of Ruby as something really "weird".

  7. Without getting to hairy details, you can assume Ruby follows SemVer in terms of Ruby API. What can break is certain C-level API (check the changelogs of 2.1, 2.2 and 2.3). Also, if an application relies on RUBY_VERSION to be 1.8.7, then changing it to 2.3.1 is not "breaking backward compatibility", even if the app was broken. This means "compatibility" is an "agreement" on both sides. Balance and fairness. This means some effort should be put in by users to make their apps easier to upgrade to newer Ruby versions. It doesn't make sense to make life completely "effortless" and "lazy" for them, while contributors get unfairly "accused" of "breaking things".

  8. End users prefer newer Rubies to be supported. Otherwise they're deprived of many benefits, such as performance, lower memory usage, cleaner code, etc. If a codebase has to stay 1.8.7 compatible, there are a lot of 2.3 benefits the user WON'T get because it requires actually changing your codebase. That's unfair, especially since even the ruby-core team doesn't refuses to work on Ruby < 2.2 anymore. And it's unfair to expect them to.

  9. Ruby 2.2 is officially "recommended" - but that's actually an understatement, because the recommendation is coming not from a "random Internet user", but from the Ruby core developers. Supporting something NOT recommended by the Ruby core team is perhaps arrogance.

  10. The "cult of backward-compatibility" practically seems to stem from complains from non-expert devops complaining about their Ruby 1.8.7 or 1.9.3 CI builds "breaking". "Provisioning" and "production" are 2 unrelated terms. In devops, it completely ok for a failure in "provisioning". Because it means a failure in production was avoided. (There are usually rollback-mechanisms in place for this). This means dropping support for all Rubies < 2.2 is perfectly technically. The ONLY reason to support earlier Rubies is to help users migrate ASAP. But if someone STILL hasn't migrated from 1.8.7 - and they aren't a major sponsor or contributor to the community ... then I can't find another explanation other than ignorance.

  11. Ruby 2.3 provides massive benefits for Rails developers. How will developers feel if they have Ruby 2.3. "habits" and the master branch of your project is required to support Ruby 1.8.7 (Or anything < 2.2, really)? Which version of your project will finally be "2.3 only"? How many years will it take before developers can start using "2.3 only" code in your project? Will the Ruby community still exist by then?

  12. There isn't any demonstrable benefit to keeping backward compatibility in this case. All arguments I've encountered a just impractical ideology and "broken builds" (which are "broken" because they're doing what they should - preventing BAD combinations of Ruby and gems from getting installed by users). In compiled languages, it's unreasonable to expect multiple incompatible libraries to be supported by a single compiled library. And especially not "forever". Multiple versions is like multiple API's - there should be only "one" version of Ruby per gem version, otherwise SemVer doesn't make sense.

Summary: Ideally, there should be a "Ruby 2.2 only" branch of your project with actual releases available. If you don't have time for migrating, you certainly don't have time for the illusion of "backward-compatibility". Trying to "implement" Ruby 2.2 features inside Ruby 1.8.7 "code blocks" (using 'if' statements) will just get more and more frustrating with every PR. And you don't need a major bump to drop support for Ruby 1.8.7. (Or anything < Ruby 2.2).

That should help create a branching policy with a lot less worry and burdens no one really needs.

For a gem that helps understand and manage such issues, see: https://github.com/e2/ruby_dep/blob/master/README.md

@smostovoy
Copy link

+1

@cwarner-mdsol
Copy link

mmhmm 👍

Copy link

ghost commented Jan 23, 2018

I wonder how it will be with Ruby 2 and Ruby 3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment