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