The Crystal Programming Language Forum

Artificial shard restrictions (version: <= 2.0.0) considered harmful

Disclaimer: I’ve written about similar problems but not this specific issue of false forward incompatibility. Solutions were proposed later in the discussion but no one seems to like them. My take is people don’t like change and the arguments against are very similar to those I heard against automated testing back in 2005 (Where I worked tests were done manually by clicking around). Only in this case there are no products (crystal 2.0.0 doesn’t exist) and no tests. Just made up numbers.

Disclaimer: This post contains humor and may cause allergic reactions, priapism or death in people with prexisting sticks up their butts.

Problem

Many shards are adding restrictions on future versions of crystal. Example:

- `crystal (>= 0.34.0, < 2.0.0)` required by `json_mapping 0.1.1`

After a major release this is problematic because

  • No one knows if any shard will or won’t work without modification in crystal 2.x, 3.x, n.x. So the version restriction are guesses at best (I would consider it a lie)
  • Unmaintained shards stop working (without ignoring crystal version restrictions). It’s fairly common in various languages to have simple libraries that everything depends on unmaintained and without new releases for years but still functioning perfectly. Artificial shards restrictions breaks this and forces maintenance.
  • Shards with maintainers are forced to release on crystals release schedule. For open source software with volunteers that’s hard(er) to get everyone on board especially when life happens (new job, new home, baby etc). Some may wait weeks/months/forever for updates.
  • Confidence is lost in shard version restrictions. Is it really incompatible or just complaining? Maybe i should always turn it off?
  • Arguing ensues in organizations using crystal on how/when to upgrade (I’ve been through these in ruby and more). Real example. Enterprise software was 4 years out of date in php & 2 years in ruby. Do we upgrade the interpreter (compiler for crystal) 1 version at a time or jump several versions? Now hypothetically enter the shards restrictions. Now you need to upgrade every shard with an artificial restriction at the same time (along with dependencies). Want to upgrade one shard at a time? Fuhgeddaboudit. Putting each shard upgrade in it’s own commit with automated testing? Not happening. git bisect to tell you which shard upgrade introduced bugs? Impossible because the newer shards may have API changes.

Already this is generating support requests and confusing users from shards with restrictions of <= 1.0.0 after the 1.0.0 release. Expect a greatly increased number of users complaining about broken shard dependencies when 2.0.0 is released .

Let’s take a ruby example and later compare with crystal equivalents.

  1. A 3 year old sinatra application depends on gems A, B & C
  2. Normally I could upgrade ruby and most gems will work. Maybe C doesn’t
  3. The new version of gem C compatible for new ruby interpreters requires API changes. So I upgrade C leaving everything else alone and test it for n days/weeks.
  4. Next I upgrade the ruby interpreter 1 version at a time. Testing for n days/weeks for each version.
  5. Done.

Compared to crystal with artificial shards restrictions.

  1. A 3 year old kemal application depends on shards A, B & C
  2. A B & C all tell me they can’t run on a new crystal compilers. Enter office politics. Should the warnings be ignored, Do you upgrade everything at once or try to upgrade one at a time? Should we wait for new versions of A & B which don’t have any version available for the newest crystal release?
  3. Your team decides to wait. 3 weeks pass. During the wait Andrew wanders off and is eating by grue.
  4. Your berserker gets tired of waiting and attempts to upgrade C. It doesn’t play nicely because the new version specifies a new crystal version is required. She fails her intelligence check and isn’t sure if the dependency is real or just specified that way. While raging she commits an upgrade to C.

Do you use A) Use --ignore-crystal-version and test the upgrade in isolation or B) Upgrade the interpreter at the same time? [Press A or B to continue]

A) Your party decides to use --ignore-crystal-version
  1. It works but the build/tests and each developer needs to use --ignore-crystal-version which is a permanent spell and requires 1000xp. Hopefully someone will remember to remove it later before it causes a critical failure.
  2. Continue your adventure as with the ruby version
  3. Did everyone remember to remove --ignore-crystal-version within n months? For those that didn’t every time they add a new shard or upgrade roll a d6. On 2-3 an upgrade with an accurate shard restriction slips past and you use a version known not to work. Lose 1-4 hours of time. On 1 your critically fail. The bug was subtle, made it to a live environment and wreaked havoc.
B) Your party decides to upgrade crystal at the same time
  1. +10 fatigue/anxiety to each member handling operations.

  2. Several forks have popped up with updated shards but no word from the original shards.

  3. Do you C) Use a unofficial fork? D) Wait for an official update. [Press C or D to continue]

C) Use an unofficial fork
  1. Several forks exist. Some claim to fix compatibility with new crystal versions. Others only upgrade shard.yaml. Your wizard casts a divining spell to see which is best. Roll a d6. On 1 you choose a fork controlled by a russian crime ring illithid. Every computer that runs the new version is under a mind control spell. Lose all your bitcoin.
D) Wait for an official update
  1. The update never comes. Your party dies of exhaustion from too frequently copulating under an office desk. Next time siesta instead of viagra.

Maybe your experience won’t be as bad, but most games I DM end up with the party dying of exhaustion from excessive copulation. Maybe that’s why my gf’s won’t let me non-leather DM anymore…

Pillowtalk Solutions

  • Don’t specify future incompatibility unless you know it’s incompatible. Example. Your working on a maintenance release for a shard A release 1.x.x. Crystal 2.x.x is already out and fails it’s unit tests so a future compatibility restriction is accurate.
  • Allow mutable shard restrictions, possibly by putting shard.yaml in it’s own branch of via some other means. When 2.0.0 is released shard.yaml can be updated for all past versions that are incompatible by testing rather than guessing.
  • Write shard helper command to test each release and update the shard restrictions. Example logic:
foreach shard release
  `git checkout #{tag}`
  if `crystal spec`.exit_status != 0
    # change shard restriction to <= CRYSTAL_VERSION
    # preserving restrictions older than the current version
  end
  • Bonus xp awarded for testing multiple crystal compilers against each release.
  • Bonus xp awarded for generating github/travis configs using matrix tests automating the entire process.

Tags: DnD, Porn, TMI, MTAW (must try at work)

2 Likes

Related: Feature/warn only crystal version by beta-ziliani · Pull Request #496 · crystal-lang/shards · GitHub. The semantics of crystal property are going to be changing as part of the next shards release.

1 Like

This post isn’t about descriptions as much as tooling. shards attempts to resolve dependencies automatically. It can only do so if it has accurate version restrictions which aren’t provided.

My requests are:

  1. Don’t specify made up max versions. They cause more problems than they solve.
  2. Store version restrictions in a mutable place so they can update over time.
2 Likes

I mostly agree to your suggestions. IMO an upper version restriction should only be placed when you know it’s incompatible. Changes in major Crystal releases won’t affect all aspects of the language and chances are high that the same code base may work with several Crystal releases.

A part that I don’t agree on is your optimism about unmaintained shards. Just to be clear: A shard that is not maintained may work fine 99,9% of the time. But when there is a severe bug detected in an old codebase, it needs to be fixed quickly. If the shard is not maintained, that’s not going to happen. Of course, anyone can fork it and apply the fix to the fork. But that involves a lot of friction at a time when you should really just focus on a single bug-fix update.

2 Likes

I just noticed the the shards spec says:

This should be probably removed or reworded if the upper limit is to be discouraged.

Oh an GitHub - crystal-lang/shards: Dependency manager for the Crystal language itself sets an upper restriction on the crystal version.

Hum, I see how ‘upper restriction’ could be useful. This could get messy if have something like ‘my shard works with Crystal version A, B, D, G, H, but not C, E, F, I’. Hopefully this kind of thing doesn’t happen often. But, in this case, probably best to keep it simple and version bump the shard and go with ‘>= G, < I’ for the Crystal version.

That causes problems.

  1. My application uses shardfoo v1.0.0 which restricts the crystal version to < 2.0.0
  2. shardfoo releases v2.0.0 before crystal 2.0 because it broke API compatibility.
  3. shardfoo releases v3.0.0 before crystal 2.0 because it broke API compatibility again
  4. crystal 2.0.0 is released.
  5. shardfoo releases v3.0.1 only to advance the crystal compatibility version to another lie of < 3.0.0

Now try upgrading your application to crystal v2.0.0. What happens? shards complains shardfoo v1.0.0 & v2.0.0 are incompatible.

Do you upgrade to shardfoo 3.0.1? The API has changed twice. Now there’s more work to do and less ability to bisect, or test one change at a time. How many shards do you have to do this for? 4? 5? 15? Multiply the additional work, lack of testing one change at a time by every shard and their dependencies when it’s time to upgrade. What a mess.

Or you ignore the warnings, keep shardfoo at 1.0.0 which functions perfectly under crystal 2.0.0. So users lose faith in what shards is telling them, because it lied to them (it repeated a lie the author put in)

Conclusions:

  • crystal compatibility should be based on testing not guessing by having a mutable shards restriction that can update when new versions of crystal (or other shards) are released.
  • Don’t lie to your users. They’ll stop trusting what shards tells them and then what’s the point of the version restriction if it’s not trustworthy and needs to be overridden? I’m already ignoring it for all the shards that say < crystal 1.0.0 now that 1.0.0 is out.

If ruby did this everything would break the moment a new ruby version is released.

1 Like

To put it simply: Would you tolerate Excel refusing to run when a new version of windows is released, not because the software broke but because someone added an artificial restriction on the windows version Excel works with?

No? Then don’t do it here.

shards itself having a restriction is possibly the only restriction that makes sense since

  1. They’re maintained by the same team and released at the same time.
  2. shards is a precompiled application. You shouldn’t hit a crystal restriction unless you’re a package maintainer and if you do you probably mismatched your shards and crystal versions.

There are more than a few examples of crystal-lang shards with artificial restrictions that probably shouldn’t be there. Thank you @szabgab for looking.

Let’s take crystal-mysql as an example. Eventually libmysql will release an incompatible version let’s say v100.0.0. At some point crystal-mysql will stop providing backwards compatibility with version < v100.0.0

What if I need an old libmysql that’s compatible with an ancient mysqld accounting system? Newer shards won’t provide compatibility and older shards all have fake restrictions on the crystal version. Now I’m stuck.

Applications can sit around for years without being upgraded running on old systems until it dies, gets hacked, a security audit, moving to a new container infrastructure, needing new features and entering active development again or other reasons. During that time lots (3-5ish?) crystal releases may occur, lots of shards bindings are released and their underlying libraries go through incompatible releases as well.

Fake shard restrictions only get in the way and generate support requests.

I am working on a web site that is analyzing the source code of shards. It is still far from being usable, but it now has a page that lists statistics about the “crystal” field in shards.

I hope it can already provide some value to this discussion. I’d be happy to further fine-tune the statistics and/or provide ways to list the various version requirement formats.

These statistics are already available at Shardbox: Stats

That’s nice and also: Oh, so I was wasting my time :frowning_face:

I’m sure you learned a lot ;) I’ll look into your code, maybe some ideas can be really useful. There is definitely a benefit from someone starting a similar project from scratch and having different ideas about how to do things.

2 Likes