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