this repo has no description
1+++
2title = "Let's talk about `application/0`"
3date = 2019-07-26
4description = """
5Have you ever thought about that one simple function in your `mix.exs`? It
6comes out as quite powerful and useful place for storing configuration and
7post-launch scripts."""
8
9[taxonomies]
10tags = [
11 "elixir",
12 "erlang",
13 "beam",
14 "programming"
15]
16+++
17
18When you start your new Elixir project via `mix new my_awesome_project` you will
19end with something like this in `mix.exs`:
20
21```elixir
22defmodule MyAwesomeProject.Mixfile do
23 use Mix.Project
24
25 def config do
26 [
27 name: :my_awesome_project,
28 # …
29 deps: deps()
30 ]
31 end
32
33 def application do
34 [
35 extra_applications: [:logger]
36 ]
37 end
38
39 defp deps do
40 [
41 # …
42 ]
43 end
44end
45```
46
47And in most cases you will focus on `deps/0`, sometimes on `config/0`, but the
48`application/0` you will almost never touch, and if you do, you probably will only
49need it to add new entry in `:extra_applications` or sometimes
50`:included_applications`. Except for, that this function interns are terra
51incognita, a place where you never look unless you are forced to, like "Read
52it later" list in Safari. This is sad, as it is quite powerful and useful
53piece of code.
54
55However first things first.
56
57## What `application/0` is for?
58
59In Erlang the running system is built from applications. Those applications are
60like processes in your system managed by your system supervisor (SysV, systemd,
61OpenRC, launchd, etc.). They are launched either during VM startup or on
62direct user request via `Application.start/1-2` and its family. Starting
63this application is described in `my_awesome_app.app` file which is commonly
64generated by the build system from the template. In Rebar3 projects this
65template is `src/appname.app.src` and in Elixir it's the return value of the
66named `application/0` function. This generated file is known as [Application
67Resource File][app file]. It is just tuple in form of `{:application,
68:my_awesome_app, opts}` where `opts` is a keyword list of additional properties
69(to be exact it's output of the `application/0` function).
70
71Two of those optional fields are quite known in the Elixir community:
72
73- `:mod` which is a tuple `{module(), term()}` containing the starting point
74 of our application; in most cases this is the module that will return main
75 supervisor of the application.
76- `:applications` contains all applications required by our application; younger
77 Elixir developers possibly never seen that as since version 1.5 this field
78 is automatically filled by parsing `:deps`, though we still can add entries
79 there via `:extra_applications`
80
81There are also few Elixir specific fields, sometimes used in larger projects:
82
83- `:extra_applications` - those should be included in the release
84 and automatically started before running current application; but aren't in the
85 dependencies list because, for example, are in default distribution, for
86 example `logger` or `inets`.
87- `:included_applications` - applications which should be included in the
88 release, but not automatically started on boot.
89
90Wait, there is more!
91
92Unfortunately not all of the highly useful keys are used/known in the community.
93
94## Application environment
95
96For some reason everyone calls it configuration, so if you are familiar with
97`config/config.exs` and for some reason you decided to go [against the *Library
98Guidelines*](guidelines) and you have decided to use application environment for
99configuring your code (there are reason to do so, even when you publish your
100code as a "library", see `lager` or `opencensus`) then you will soon find that
101putting configuration in your's library `config/config.exs` do not matter much
102in it's dependants. You have 2 possibilities how to solve that:
103
104- Use `Application.get_env/3` and define your "default" as a 3rd argument.
105- Use `:env` to set data that should be loaded to application environment by
106 default.
107
108Be wary that the second option works only for current application, so you
109cannot configure other applications (for example `logger`) there. *But what is
110the point?* You may ask, and I found one. If you want to use default
111`sys.config` file for configuring your application then sometimes few pointless
112configuration option can land there, like `:ecto_repos` variable which truly
113doesn't matter much in production as it's only used by Ecto's Mix tasks. What
114I do is to add new entry in `application/0` with:
115
116```elixir
117env: [
118 ecto_repos: [MyAwesomeApp.Repo]
119]
120```
121
122And call it a day. Now I can focus on keeping **real** configuration options in
123`config/config.exs` and remove unneeded fields from there. But remember that you
124still can override these values by setting them in `sys.config` if needed, so
125this is pure win for me.
126
127## Start phases
128
129Application configuration also allows you to define additional pieces of code to
130be run after your application started. For example imagine situation when you
131want to send Slack notification that given node started and is ready to work.
132You can do it via temporary task in `Supervisor.init/2` by defining child list
133like:
134
135```elixir
136[
137 MyApp.Repo,
138 MyApp.Worker,
139 {Task, &send_slack_notification/0}
140]
141```
142
143Alternatively you can use `:start_phases` in `application/0`:
144
145```elixir
146start_phases: [
147 notify: []
148]
149```
150
151And then define in your `Application` module function `start_phase/3`:
152
153```elixir
154def start_phase(:notify, :normal, opts) do
155 :ok = send_slack_notification()
156end
157```
158
159Where 1st argument will be the name of the phase, 2nd will be start type the
160same as in [`Application.start/2` callback](https://hexdocs.pm/elixir/Application.html#c:start/2),
161and 3rd is the value passed in `:start_phases`.
162
163The awesome part there is that `start_phase/3` is called not only for current
164application, but all of it's dependencies as well.
165
166## Registered names
167
168This is one of the things that had more sense in Erlang world than in Elixir,
169but by being good citizen we should use it as well. This is a nice solution for
170lack of namespacing in Erlang - it allowed release tools to detect collisions in
171named processes. This is simple list of atoms that contain all names that are
172globally registered by this application. Example form [Elixir's
173Logger](https://github.com/elixir-lang/elixir/blob/ee9f38635e9a6c816adb575fc9431ded49be8032/lib/logger/mix.exs#L14):
174
175```elixir
176registered: [Logger, Logger.BackendSupervisor, Logger.Supervisor, Logger.Watcher]
177```
178
179Unfortunately Phoenix do not use this field itself and do not suggest using one
180in it's default project generator. But in general it's good practise.
181
182## Summary
183
184There is much more in application configuration to what most Elixir code is
185using, it is worth sometimes to read how your application is defined and ran.
186For more information about generating application description file you can check
187out [`mix help compile.app`](https://hexdocs.pm/mix/Mix.Tasks.Compile.App.html).
188
189[app file]: http://www.erlang.org/doc/design_principles/applications.html#application-resource-file
190[guidelines]: https://hexdocs.pm/elixir/library-guidelines.html