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