Asdf: Switching between projects is breaking my system

I think this speaks broadly to my discussion with @outlog yesterday.

Why in the world would my phoenix project have elixir: "~> 1.4", in my project def? If the tilde specifies a rolling update dependency, then how do I even know which version of elixir my project is using?

Because something in your project requires features that are only available in 1.4 onwards.

elixir --version.

Basically, its always the elixir thats available on your system.

But this elixir will yell at you when you try to use it in a project that it is not compatible with.

I’m wondering though why 1.4 isn’t yelling at you because of the gettext requirement of elixir ~> 1.6

Basically, its always the elixir thats available on your system.

The elixir on my computer could be different from my neighbor’s. So where can we specify that this project should run on Elixir X.X? Since that seems to be the heart of the problem…

Isn’t this the de facto use case for using Docker images? Since Elixir carries a number of breaking changes, shouldn’t all projects specify a specific version?

That’s where you use the .tool-versions for, but that is not a guarantee.

If you build a release it will have the elixir bundled in that was used to build the release. That’s all you will get.

Of course you can use external means of providing exact versions like docker or nix, but even with those it has to be done right.

1 Like

PS of course you can set the elixir constraint to elixir: "== 1.4.4", but probably every one will ignore it, as it’s usually a useless restriction.

It does seem like some of your dependencies have moved on from Elixir 1.4 so you’d do well to at least go to 1.6 as gettext seems to imply that you should.

Is that a problem for your project?

Well actually I’m on 1.6.6 but I didn’t realize ~> 1.4 meant anything above it. I ultimately had to look at my Docker build in my CI to determine which versions of Erlang/Elixir was being used upstream.

Not it doesn’t.

~> X.Y means >= X.Y.0 && < (X+1).0

https://hexdocs.pm/elixir/Version.html#module-requirements

Well considering 2.0 doesn’t exist yet … it currently means anything :slight_smile:

No, the ~> constraint has a well defined meaning.

If 2.0 got released in an hour and you try to update an hour later, you wont get that update without adjusting the constraint.

I’m just gonna say it’s absolutely bonkers to bootstrap a fresh Phoenix project, then switch to a different project with a different version, then go back to the original and have everything break because you forgot which specific Elixir+Erlang versions you used. Why would .tool-versions not be included out of the box here?

Because asdf and it’s .tool-versions is only one of thousand ways to specify the dependency to the runtime.

As a rule of thumb the generator will always set it’s own version as the semantic bound.

Any way is better than no way. I have no way of determining what version this project was built under…?

You usually do not need to.

And as I said, you can have a quick guess for the version that was used to create the project by looking at the :elixir constraint in the mix.exs, its usually set to the semantic bound of the version that created the project.

Other languages and runtimes give you much less of hints, while beeing much more problematic with updates.

Don’t blame the language when you can’t focus long enough to finish your bootstrapping.

There are a couple of tools available, that are able to manage your elixir and erlang versions, some of them manage both, some manage only one and you therefore need a tool for each. How to bootstrap accordingly depends on the choice of your tools.

There is a good reason for the language to not put one of those tools over the other.

I just installed a Phoenix project with elixir 1.9 and the mix.exs shows “~> 1.5”, so that’s not very helpful.

You can’t focus on something you aren’t aware that you’re suppose to keep track of!

You usually do not need to.

You don’t need to keep track of what elixir/erlang versions you use? I would think it’s essential. If I’m handing off my project to another team of developers they won’t know which elixir/erlang versions to run! Am I missing something?

Okay, didn’t know that the phx_new generator uses a different method to put the constraint. Indeed thats a pity.

Personally I used .tool-versions, but slowly migrating my projects over to nix.

I’m used to putting runtime/compiler requirements into external tools since beginning of time. I never had a language that did that for me.

Only rust tries to do so with rustup, but still, it does not do so in the project, it stores this information locally on my computer. It can’t be made part of the repository/declaratively.

As a developer you should specify a lower bound of the runtime version because of technical requirements, eg. do I need defguard? So we have to use elixir: "~> 1 .6". We also need :persistent_terms, thats OTP 20 IIRC. 1.6 can cope with that. No need to change. This has to be documented as minimum requirements.

From then on it should be irrelevant which version set is actually used, as long as it is within specified bounds.

Your developers should be able to use elixir 1.6, 1.8, or even master without any problems. As long as they are careful and only use features that have already been available in 1.6, which has to be checked by CI.

Also you should check some more recent version on CI, to make sure you are ready for updates, and as a plus you also check against master to early recognize problems that a future version might bring, and even report bugs early.

I wouldn’t be complaining if it wasn’t interfering with my dev workflow but I hope my comment gets heard that for the projects I’ve worked on, the dependencies in mix.exs will only work under a specific elixir/erlang environment. Broken dependencies are the bottleneck of so many projects. I will obviously be tracking this now, but I’d be surprised if i was alone with this concern. Nevertheless thanks for your insight.