sasajuric

sasajuric

Author of Elixir In Action

Rethinking app env

This is a spin-off from the discussion about the new config proposal. I’m replying to this post by @michalmuskala separately, to avoid noise in that thread.

Currently, it seems to be a bucket for all sorts of things, which includes system configuration. I definitely agree that it’s not a good place for operator configuration.

This sounds vague, could you elaborate what kind of loose coupling do we achieve by e.g. having pubsub: [name: UI.PubSub, adapter: Phoenix.PubSub.PG2] in config?

What is that “everything else”? What’s the criteria for defining a parameter in the config, and not in the code? For example, why should a pubsub name and adapter go to config, but e.g. supervisor name and restart intensity be in the code?

First Post!

Qqwy

Qqwy

TypeCheck Core Team

A thing I’d like to mention here since it definitely does not belong in the proposal-topic, although it might also be a bit tangential to your posts, @sasajuric (but I first started thinking about this during reading your blog article) :

It actually strikes me as weird that there are so many configuration settings that will require an os-application to completely restart. To be perfectly honest, even if parts of the os-application (some of the beam-applications) require configuration to happen at compile-time, shouldn’t we find a way to re-compile them at runtime instead?

Most Liked

bitwalker

bitwalker

Leader

I think a great deal of the abuse of the application env occurs because it is simply easy to do so. It is more difficult to think about how to make your library or application configurable by feeding in parameters to a top-level supervisor or something, than it is to just stuff anything configurable into config.exs and use Application.get_env/3. As long as the easier path is available, there will always be people who take it. Erlang didn’t have quite as much of an issue with this, because it was more of a pain to use the application env for configuration than it was to configure via parameters.

I think a related issue is compile-time configuration. Some of the examples José mentions need configuration inputs at compile-time, and there is no other means to provide them except config.exs, since Mix evaluates it during compilation. Had Mix gone the route of Erlang in regards to application env, the whole discussion would be moot, since config.exs would be limited to runtime configuration. Since it did not, we also have the problem where config.exs has become the catch-all for anything configurable. People are using System.cmd/3 to get a git shorthash in config.exs, rather than doing something like -Dcommit_hash=$(git describe --tags --long) as a flag to the compiler, which is how such things are typically handled in just about every other language I’ve worked with. That isn’t suitable for everything though, and is unwieldy for repeated use. This is definitely a place where config.exs eases a lot of pain - compile-time parameters and global defaults.

I was hoping that by treating config.exs as a runtime config, and having mix.exs handle defining what to include or not include at compile-time, we could get the best of both worlds. It doesn’t really solve the issue of how to prevent people from abusing the app env, but I’m not sure that is something we can really fix at this point, about the only time you could make that kind of change is with a theoretical Elixir 2.0, but its not clear how you could do that without making configuration more inconvenient in general. It seems like a bit of a catch-22 to me.

12
Post #3
sasajuric

sasajuric

Author of Elixir In Action

I think that this cuts to one of the root causes why config scripts are used so much.

So to be clear, my opinion is that config scripts are way overused, and contain a lot of stuff that doesn’t belong there. I consider them a pile of bloat arbitrarily thrown together, and I think that they often make the code more difficult to understand.

IMO, there are a couple of reasons why so much data ends up in config scripts:

  1. Some of our flagship libraries (Phoenix and Ecto) promote it.
  2. Some libraries require it.
  3. There’s no obvious or convenient way to specify variations between different mix envs.

To be completely honest, at my company we also overuse config scripts, precisely for the reasons stated above. So for example, even though we’re aware of if Mix.env() and how/when we can use it, if a constant varies between different envs, we usually just stuff it to config script.

I further believe that this convenience subtly creates a tendency to put even more data into config scripts. So for example, my colleague recently argued that some MFAs belong to config scripts, because they “feel” like config, even though MFA is clearly code, not config. This was for me the direct motivation to write that lengthy post about config scripts, and to actively start questioning them.

To be clear, I used them myself a lot, even though I was never quite comfortable with them, which is why I’m increasingly starting to feel that they are misleading people and causing problems much more then they actually help because:

  1. Config scripts are not runtime friendly, and cause confusion when used with OTP releases.
  2. Parameters are not consolidate anymore. For example, endpoint parameters are now specified in at least four config files.
  3. A bunch of unrelated data is thrown together.

Therefore, I feel that instead of adding the additional complex machinery to address the issue number 1 (which is just one issue of config scripts), it would be much better if runtime configuration through regular code was promoted and assisted.

In particular, I’d like to see:

  1. Official helper macros to simplify expressing small variations between mix envs in regular code. Perhaps something along the lines of this macro, or something different/better.
  2. mix phx.new, Ecto/Phoenix docs, Elixir docs, and official getting started guides favouring runtime configuration over config scripts.

This will not solve all the problems, but I believe it will solve most of them, and that it will guide the community to write their code and libraries in a better way.

Now, some members from Elixir and Phoenix core team have mentioned that many people were further confused when init/2 callbacks were introduced. Personally, I don’t at all buy that this means that runtime config is confusing. The thing is that prior to init/2 a typical Elixir project had at least four files where parameters were specified (config, dev, test, and prod.exs). And then the fifth one was added. No wonder that people found this even more confusing.

But until we guide people to provide their parameters as much as possible in the regular code, it’s unfair to say that runtime config is confusing.

I should also state that I was not a fan of init/2 when it was proposed. Personally, I felt that endpoint and repo should just take their parameters as argument to start_link and child_spec/1. This has issues with hot code reloading, but that’s an advanced scenario anyway. My impression is that with init/2, Elixir/Ecto/Phoenix team decided to make the runtime configuration more complex to simplify advanced (and arguably infrequent) scenarios at the expense of more complex interface for everyone. My feeling is that this was not a good tradeoff. I suspect that the callback style interface of init/2 also adds to the confusion people have.

In summary, I think that Elixir/Phoenix/Ecto team historically favoured config scripts way more than runtime configs in regular code, and that’s why we’re here. Perhaps, instead of adding more complex machinery to config scripts, a better way would be to assist and promote runtime configuration through regular code and plain old passing of arguments to functions. I feel that this requires much less interventions in the language and that it can take us very far, although it will admittedly take time to move the community to such style of configuration.

sasajuric

sasajuric

Author of Elixir In Action

Yes, as the title says, this thread is about app env and it’s closely related cousins config scripts.

Just to be clear, I don’t deny that there’s a thing called system configuration. I just think that app env and config scripts are in most cases an inferior mechanism for managing the system configuration. Moreover, I think that the defaults promoted by e.g. phx.new are arbitrary, confusing, and at the same time limiting.

In my impression, a bunch of stuff ends up in config scripts for dubious reasons, and they don’t even help with actual system configuration (assuming you want to run OTP releases). I’ve been personally confused by config scripts, the team I’m a part of has been confused by config scripts, I’ve seen other people being confused by them, and a lot of libraries promoting them for no particular reasons.

I think we need to strongly challenge this approach, and reevaluate do we really need to promote config scripts and app env so much. I don’t say that they are useless, but I think that in most cases they are not the most appropriate choice. I think that we should instead promote regular code as much as possible, and leave the special code for the few cases where it’s really needed.

Last Post!

sasajuric

sasajuric

Author of Elixir In Action

If a library Foo requires some parameters which you want to fetch from external sources you can invoke e.g. Foo.bar(fetch_params_from_external_source(...))?

Where Next?

Popular in Discussions Top

ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
pdgonzalez872
If this has been asked here before, please point me to where it was asked as I didn’t find it when I searched the forum. Maybe a mailing ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New
lucaong
Hello Elixir and Nerves community, I have been working for a while on an open-source embedded key-value database for Elixir, that I call...
230 14403 124
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 55125 245
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New

We're in Beta

About us Mission Statement