Over the last releases we have been making improvements to the
configuration system. However, there is still one large gap to be
addressed. Today config files are treated as compile-time configuration by
default. This behaviour, alongside overuse from libraries, made using the
application environment via config files more rigid and error prone than
they have to be.

Today, Mix will load "config/config.exs" before any of the dependencies are
compiled and on every command. This makes basic scenarios like the ones
below impossible:

   -

   If your project requires some environment variables in development, you
   cannot enforce them. Otherwise, basic commands such as mix help or
   editor integration will stop working


   -

   If you need to configure one application based on another application,
   that is impossible to do as well, as no applications are available at this
   point

Also, because configuration files run during compilation by default,
library authors often accidentally rely on compile-time configuration.
Luckily, Elixir v1.10 has added Application.compile_env/2. In the future we
will deprecate Application.get_env/3 in the module body, which will help
steer people to the correct direction in their own applications.

On the opposite side, we have releases, where the configuration works at
runtime only. Given there is no way to execute configuration at runtime in
Mix, we end-up with two completely disjoint approaches. This introduces a
couple issues of their own:

   -

   Since "config/releases.exs" run only during releases, if you have a
   syntax error (or similar), you will find about it just way too late


   -

   There is no way for frameworks like Phoenix to provide a configuration
   file that works for both Mix and release deployments

Our goal is to address these issues. However, it is important to consider
that a complex project today may already have many configuration files:

   - config/config.exs (compile time) - shared configuration settings
   - config/{dev,test,prod}.exs (compile time) - per env configuration
   settings
   - config/releases.exs (runtime) - release specific configuration settings

Therefore, we would like to propose a new configuration file that can
address the problem above while replacing the need to use
"config/releases.exs" in 99% of the cases.
Proposal: introduce config/runtime.exs

Our proposal is simple: we will introduce "config/runtime.exs".
"config/runtime.exs" will be loaded both by Mix and Releases, closing the
gap between them.

For Mix, "config/runtime.exs" will load after the code is compiled and
before the application starts, this allows "config/runtime.exs" to rely on
code from dependencies, as long as you keep in mind that any application
that is started during "config/runtime.exs" cannot be configured by
"config/runtime.exs" itself. Furthermore, given "config/runtime.exs" works
at runtime, changing it won't require the whole application to be
recompiled.

For Releases, it will work precisely the same as "config/releases.exs". If
both are available, "config/runtime.exs" is executed first, followed by
"config/releases.exs".

There are a couple pitfalls to be aware though:

   -

   Since "config/runtime.exs" is used by both Mix and releases, it cannot
   configure :kernel, :stdlib, :elixir, and :mix themselves. Attempting to
   configure those will emit an error. For those rare scenarios, you will need
   to use "config/releases.exs" - but "config/releases.exs" will remain
   simple, which will reduce the odds of syntax errors.


   -

   Since "config/runtime.exs" is used by both Mix and releases, it cannot
   invoke "Mix" directly. Therefore, for conditional environment compilation,
   we will add a env/2 macro to Config that will be available for all
   config files. For example, instead of a "config/runtime.prod.exs", one will
   have to:

   import Config

   env :prod do
     config :my_app, :secret_key, System.get_env!("SECRET_KEY")end


One may argue that "config/runtime.exs" should eventually replace
"config/config.exs" as the default file for application configuration. We
will certainly evaluate this option in the future but it is important to
take baby steps. And the first step is to support "config/runtime.exs". :)
Implementation considerations

This section covers implementation details. It is not part of the proposal
per se. Although the feature is relatively small, it requires many
improvements to Mix and the underlying config engine. The tasks are:

   - Load config/runtime.exs inside Mix
   - Copy config/runtime.exs inside escripts and load them when the escript
   runs
   - Copy config/runtime.exs inside releases (similar to
   config/releases.exs)
   - Add a feature to Config.Reader that allows a warning to be emitted if
   an undesired module is used (for example, Mix)
   - Add the env/2 macro to Config
   - Raise if "import_config" is used in "config/runtime.exs" and
   "config/releases.exs" - providing proper guidance to users

One aspect to consider is exactly when runtime config should be loaded
inside Mix. We need to choose between doing it after the "compile" task or
before "app.start". The issue is that many projects have tasks that only
need the application to be compiled but not started. For example, Ecto Repo
management tasks or Phoenix routes tasks. Those tasks today simply run
Mix.Task.run("compile"). However, if we were to introduce
"config/runtime.exs" and load it before "app.start", those tasks will now
run without "config/runtime.exs" and behave incorrectly.

Therefore there is an argument to be made to load the runtime configuration
right after the code is compiled - even though this is a bit unintuitive.
The other option is to ask users to always run "app.start" as the entry
point and pass the "--no-start" if they actually don't want to start their
apps, which is also a bit counter intuitive. Unfortunately, the second
option means projects will behave incorrectly until they are updated.

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2BS7nefgLCXCb9OWQGj9d9eE1TrDstq%3Dkm%3D%2B2a7iT9DAw%40mail.gmail.com.

Reply via email to