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.