+++ title = "Let's talk about `application/0`" date = 2019-07-26 description = """ Have you ever thought about that one simple function in your `mix.exs`? It comes out as quite powerful and useful place for storing configuration and post-launch scripts.""" [taxonomies] tags = [ "elixir", "erlang", "beam", "programming" ] +++ When you start your new Elixir project via `mix new my_awesome_project` you will end with something like this in `mix.exs`: ```elixir defmodule MyAwesomeProject.Mixfile do use Mix.Project def config do [ name: :my_awesome_project, # … deps: deps() ] end def application do [ extra_applications: [:logger] ] end defp deps do [ # … ] end end ``` And in most cases you will focus on `deps/0`, sometimes on `config/0`, but the `application/0` you will almost never touch, and if you do, you probably will only need it to add new entry in `:extra_applications` or sometimes `:included_applications`. Except for, that this function interns are terra incognita, a place where you never look unless you are forced to, like "Read it later" list in Safari. This is sad, as it is quite powerful and useful piece of code. However first things first. ## What `application/0` is for? In Erlang the running system is built from applications. Those applications are like processes in your system managed by your system supervisor (SysV, systemd, OpenRC, launchd, etc.). They are launched either during VM startup or on direct user request via `Application.start/1-2` and its family. Starting this application is described in `my_awesome_app.app` file which is commonly generated by the build system from the template. In Rebar3 projects this template is `src/appname.app.src` and in Elixir it's the return value of the named `application/0` function. This generated file is known as [Application Resource File][app file]. It is just tuple in form of `{:application, :my_awesome_app, opts}` where `opts` is a keyword list of additional properties (to be exact it's output of the `application/0` function). Two of those optional fields are quite known in the Elixir community: - `:mod` which is a tuple `{module(), term()}` containing the starting point of our application; in most cases this is the module that will return main supervisor of the application. - `:applications` contains all applications required by our application; younger Elixir developers possibly never seen that as since version 1.5 this field is automatically filled by parsing `:deps`, though we still can add entries there via `:extra_applications` There are also few Elixir specific fields, sometimes used in larger projects: - `:extra_applications` - those should be included in the release and automatically started before running current application; but aren't in the dependencies list because, for example, are in default distribution, for example `logger` or `inets`. - `:included_applications` - applications which should be included in the release, but not automatically started on boot. Wait, there is more! Unfortunately not all of the highly useful keys are used/known in the community. ## Application environment For some reason everyone calls it configuration, so if you are familiar with `config/config.exs` and for some reason you decided to go [against the *Library Guidelines*](guidelines) and you have decided to use application environment for configuring your code (there are reason to do so, even when you publish your code as a "library", see `lager` or `opencensus`) then you will soon find that putting configuration in your's library `config/config.exs` do not matter much in it's dependants. You have 2 possibilities how to solve that: - Use `Application.get_env/3` and define your "default" as a 3rd argument. - Use `:env` to set data that should be loaded to application environment by default. Be wary that the second option works only for current application, so you cannot configure other applications (for example `logger`) there. *But what is the point?* You may ask, and I found one. If you want to use default `sys.config` file for configuring your application then sometimes few pointless configuration option can land there, like `:ecto_repos` variable which truly doesn't matter much in production as it's only used by Ecto's Mix tasks. What I do is to add new entry in `application/0` with: ```elixir env: [ ecto_repos: [MyAwesomeApp.Repo] ] ``` And call it a day. Now I can focus on keeping **real** configuration options in `config/config.exs` and remove unneeded fields from there. But remember that you still can override these values by setting them in `sys.config` if needed, so this is pure win for me. ## Start phases Application configuration also allows you to define additional pieces of code to be run after your application started. For example imagine situation when you want to send Slack notification that given node started and is ready to work. You can do it via temporary task in `Supervisor.init/2` by defining child list like: ```elixir [ MyApp.Repo, MyApp.Worker, {Task, &send_slack_notification/0} ] ``` Alternatively you can use `:start_phases` in `application/0`: ```elixir start_phases: [ notify: [] ] ``` And then define in your `Application` module function `start_phase/3`: ```elixir def start_phase(:notify, :normal, opts) do :ok = send_slack_notification() end ``` Where 1st argument will be the name of the phase, 2nd will be start type the same as in [`Application.start/2` callback](https://hexdocs.pm/elixir/Application.html#c:start/2), and 3rd is the value passed in `:start_phases`. The awesome part there is that `start_phase/3` is called not only for current application, but all of it's dependencies as well. ## Registered names This is one of the things that had more sense in Erlang world than in Elixir, but by being good citizen we should use it as well. This is a nice solution for lack of namespacing in Erlang - it allowed release tools to detect collisions in named processes. This is simple list of atoms that contain all names that are globally registered by this application. Example form [Elixir's Logger](https://github.com/elixir-lang/elixir/blob/ee9f38635e9a6c816adb575fc9431ded49be8032/lib/logger/mix.exs#L14): ```elixir registered: [Logger, Logger.BackendSupervisor, Logger.Supervisor, Logger.Watcher] ``` Unfortunately Phoenix do not use this field itself and do not suggest using one in it's default project generator. But in general it's good practise. ## Summary There is much more in application configuration to what most Elixir code is using, it is worth sometimes to read how your application is defined and ran. For more information about generating application description file you can check out [`mix help compile.app`](https://hexdocs.pm/mix/Mix.Tasks.Compile.App.html). [app file]: http://www.erlang.org/doc/design_principles/applications.html#application-resource-file [guidelines]: https://hexdocs.pm/elixir/library-guidelines.html