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]
]
endThis 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.