Why does this post exist?
I have seen many people (myself included) being confused about the
usage of :applications
vs :extra_applications
in their mix.exs
files. Why both these options are present? When to use one and when
another? Do I really need to specify any of my dependencies? Let’s clarify it.
Where to add OTP applications you depend on?
When you generate a new Elixir project with mix new
task, it will use the
following piece of code to define your OTP application in the mix.exs
file by default:
def application do
[
extra_applications: [:logger]
]
end
This replaces the previous default, where applications
was used. The
confusing part is that many libraries still write in their READMEs, that
you should add the dependencies for their libraries to applications
.
Some state you should add them to extra_applications
. So where should
you really add them?
Most likely: nowhere.
Wat
Elixir will do the heavy lifting for you. By going through the list of
your
dependencies,
and building the applications
list for you behind the scenes, all
dependencies you declared in your deps/0
function will be started
before your application is started - provided they declare OTP
application callback module to start in first place.
Let’s say you declared the following dependencies in your deps/0
function:
defp deps do
[
{:postgrex, ">= 0.0.0"},
{:ecto, "~> 2.1"},
{:bamboo, "~> 1.0.0-rc1"}
]
end
There’s no need to explicitly start postgrex
, ecto
or bamboo
by
adding them manually to :applications
nor :extra_applications
. The
:applications
list will be inferred from the deps for you (by magical
Elixir! ;)).
What if I don’t want this magic?
If, for some reason, you hate the above behavior, or it simply does not suit your needs, you can disable it either altogether or partially.
To disable the behavior altogether, provide :applications
key instead
of :extra_applications
:
def application do
[
applications: [:logger, :bamboo]
]
end
This will stop Elixir from going through the list of your dependencies and starting them all on your app start up.
You can also disable the procedure for individual dependencies. If you
want to start all of them, but say keep :bamboo
stopped, use runtime: false
option in your deps/0
function instead:
def application do
[
extra_applications: [:logger]
]
end
defp deps do
[
{:postgrex, ">= 0.0.0"},
{:ecto, "~> 2.1"},
{:bamboo, "~> 1.0.0-rc1", runtime: false}
]
end
The code above starts both :postgrex
and :ecto
(and all their
runtime dependencies) because we did not override the defaults with
:applications
list of our own. It won’t start :bamboo
, however,
because runtime: false
stops it from doing so.
Starting optional dependencies
Erlang itself ships with applications that are optional, but you don’t have to list them in dependencies. In fact there’s no way to list them I think. How do you start those then?
Add those dependencies to :extra_applications
:
def application do
[
extra_applications: [:logger, :runtime_tools]
]
end
This is precisely why :extra_applications
is there. All the optional
dependencies, either from Erlang or from your deps/0
(with optional: true
), should be added to :extra_applications
. When you do so, Elixir
will infer the list of :applications
and then merge it with list of
:extra_applications
for you. Simple!
Further reading
Post by Hubert Łępicki
Hubert is partner at AmberBit. Rails, Elixir and functional programming are his areas of expertise.