{"id":5493,"date":"2016-07-06T17:02:33","date_gmt":"2016-07-06T20:02:33","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=5493"},"modified":"2017-01-31T08:22:31","modified_gmt":"2017-01-31T10:22:31","slug":"understanding-deps-and-applications-in-your-mixfile","status":"publish","type":"post","link":"http:\/\/blog.plataformatec.com.br\/2016\/07\/understanding-deps-and-applications-in-your-mixfile\/","title":{"rendered":"Understanding deps and applications in your Mixfile"},"content":{"rendered":"
Note<\/strong>: Elixir v1.4 has been released and improves on many points touched by this article. From v1.4, Elixir will automatically infer the list of applications based on your dependencies. For more information, read the official announcement<\/a>.<\/p>\n In my journey as a curious Elixir developer, I’ve come across this seemingly simple question a few times: which Before we get down to the nitty-gritty of application dependencies, let’s first recap some basics of the initialization process in Elixir OTP applications.<\/p>\n In its essence, an OTP application is a reusable software component, consisting of multiple modules of its own and it can also depend on third-party code. These dependencies can be either library applications<\/em> \u2014 a collection of standalone modules and functions, with no processes involved \u2014 or active applications<\/em>, with their own life cycles and supervision trees. This distinction is quite subtle, it took me a while to fully grasp its implications.<\/p>\n Applications are defined with an application resource file<\/em>, like As we can see, there are some important pieces of information in it, such as the app name, version, Elixir version requirements and so on. In addition, the So far, so good. Now things start to get interesting. Let’s take a look at the Along with the That mismatch confused me. I wondered if I wasn’t mixing them up (pun intended).<\/p>\n Why should library-only applications like To support that claim, we can see that even Then make a new release:<\/p>\n Both dependencies would be missing from the final package :bomb:. We certainly don’t want that.<\/p>\n You might still ask, shouldn’t Actually, no. From the Phoenix framework standpoint, none of these applications are actually required. Cowboy isn’t a strict runtime dependency; we can freely run our Phoenix app on another compatible web server of our choice. As for Phoenix HTML and even Gettext, they are not strictly required either. We can totally build our Phoenix app without any HTML output or localization at all, consider an API-only app for example. It’s all up to us.<\/p>\n As we can see in Phoenix’s own That also explains why Cool. How come A list of all the applications currently loaded is returned when we call I am deploying to Heroku and my application requires no release packaging whatsoever, should I still worry about my list of applications? Yes, definitely. Suppose we need to upgrade some dependencies, one of them used to be just a library application<\/em> but now has become an active application<\/em>, e.g. a cache library which now has to keep a reaper process to clean up stale data. Once we upgrade the The same goes for libraries, regardless of open sourcing them or not. Remember that runtime application dependencies are transitive. Let’s say an application One of the most noticeable benefits of Elixir is all the tools-support built around it. Not only in runtime, standing<\/a> on the shoulders<\/a> of Erlang, but primarily in development and build time. In case you overlooked the announcement of Elixir v1.3<\/a> recently released, you should definitely check it out. Among the improvements in dependency tracking are two new built-in mix tasks: So, when we are evaluating these dependency issues individually, it all boils down to a few simple criteria.<\/p>\n For the For the All said and done, where can we go from here? Well, I’d say start at home, review your If you are into Elixir-Phoenix, you may also like…<\/p>\n \n<\/div>\n","protected":false},"excerpt":{"rendered":" Note: Elixir v1.4 has been released and improves on many points touched by this article. From v1.4, Elixir will automatically infer the list of applications based on your dependencies. For more information, read the official announcement. In my journey as a curious Elixir developer, I’ve come across this seemingly simple question a few times: which … \u00bb<\/a><\/p>\n","protected":false},"author":14,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[143,245],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5493"}],"collection":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=5493"}],"version-history":[{"count":23,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5493\/revisions"}],"predecessor-version":[{"id":6061,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/5493\/revisions\/6061"}],"wp:attachment":[{"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=5493"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=5493"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=5493"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}applications<\/code> from third-party libraries do I need to declare in my
mix.exs<\/code> file?<\/p>\n
my_app.app<\/code>, which is a metadata file comprised of a single Erlang term. It includes all the information needed to start our application and is managed by Mix. If you want to dig deeper into that topic you can take a look at its documentation<\/a>. Once we create a new application with
mix new my_app<\/code> a brand new
mix.exs<\/code> file is generated. This is the file that customizes how
my_app.app<\/code> will be assembled by Mix and it is the file we’ll be dealing with in the Elixir world. Here is what it looks like:<\/p>\n
defmodule MyApp.Mixfile do\n use Mix.Project\n\n def project do\n [app: :my_app,\n version: \"0.1.0\",\n elixir: \"~> 1.3\",\n build_embedded: Mix.env == :prod,\n start_permanent: Mix.env == :prod,\n deps: deps()]\n end\n\n # Configuration for the OTP application\n #\n # Type \"mix help compile.app\" for more information\n def application do\n [applications: [:logger]]\n end\n\n # Dependencies can be Hex packages:\n #\n # {:mydep, \"~> 0.3.0\"}\n #\n # Or git\/path repositories:\n #\n # {:mydep, git: \"https:\/\/github.com\/elixir-lang\/mydep.git\", tag: \"0.1.0\"}\n #\n # Type \"mix help deps\" for more examples and options\n defp deps do\n []\n end\nend\n<\/code><\/pre>\n
application<\/code> function lets us explain what is required to boot our application: which other applications need to be started before ours, locally registered processes, which module represents the starting point of our application and also some default values for the application environment. By running
mix help compile.app<\/code> we can get more information about that function. The docs<\/a> available for the
Application<\/code> behavior \u2014 which abstracts the initialization process per se<\/em> \u2014 are pretty extensive and helpful as well.<\/p>\n
mix.exs<\/code> file of a new Phoenix project. First we generate an application skeleton with
mix phoenix.new hello<\/code>, then we add
{:exrm, \"~> 1.0\"}<\/code> to our
deps<\/code>, including the Exrm<\/a> tool for generating our releases.<\/p>\n
defmodule Hello.Mixfile do\n use Mix.Project\n\n def project do\n [app: :hello,\n version: \"0.0.1\",\n elixir: \"~> 1.3\",\n # ...\n deps: deps()]\n end\n\n # Configuration for the OTP application.\n #\n # Type `mix help compile.app` for more information.\n def application do\n [mod: {Hello, []},\n applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,\n :phoenix_ecto, :postgrex]]\n end\n\n # ...\n\n defp deps do\n [{:phoenix, \"~> 1.2.0\"},\n {:phoenix_pubsub, \"~> 1.0\"},\n {:phoenix_ecto, \"~> 3.0\"},\n {:postgrex, \">= 0.0.0\"},\n {:phoenix_html, \"~> 2.6\"},\n {:phoenix_live_reload, \"~> 1.0\", only: :dev},\n {:gettext, \"~> 0.11\"},\n {:cowboy, \"~> 1.0\"},\n {:exrm, \"~> 1.0\"}]\n end\n\n # ...\nend\n<\/code><\/pre>\n
:logger<\/code> application we’ve seen before in our vanilla Elixir app, now we have a few other applications we depend upon: some from Phoenix itself, an HTTP server, some i18n<\/em> utilities and a database driver too. If we pay close attention to the
deps<\/code> and the
applications<\/code> lists, we can see it is almost a 1 to 1 ratio. Every
application<\/code> relates to its
dep<\/code> counterpart, like
cowboy<\/code>,
phoenix_pubsub<\/code>,
postgrex<\/code> and others. The exceptions are
:logger<\/code>, which is pre-built as part of Elixir<\/a>,
exrm<\/code> and
phoenix_live_reload<\/code>.<\/p>\n
Library applications must be included too<\/h3>\n
phoenix_html<\/code> and
gettext<\/code> be included in my
application<\/code> function then? It’s not that they need to be booted up and start spawning processes or something. It turns out that our
application<\/code> function has more to do with Releases<\/em> and runtime than with supervision trees. Yes, listing active applications<\/em> ensures they are started before our application, but including library applications<\/em> will also make sure they will be included<\/strong> in our installable release packages.<\/p>\n
exrm<\/code> warns us when we forget to do so. Let’s try removing
phoenix_html<\/code> and
cowboy<\/code> from our
applications<\/code>:<\/p>\n
defmodule Hello.Mixfile do\n # ...\n def application do\n [mod: {Hello, []},\n applications: [:phoenix, :phoenix_pubsub, :logger, :gettext, :phoenix_ecto, :postgrex]]\n end\nend\n<\/code><\/pre>\n
mix deps.get\nRunning dependency resolution\n* Getting phoenix (Hex package)\n Checking package (https:\/\/repo.hex.pm\/tarballs\/phoenix-1.2.0.tar)\n Using locally cached package\n...\n\nMIX_ENV=prod mix release\nBuilding release with MIX_ENV=prod.\n\nYou have dependencies (direct\/transitive) which are not in :applications!\nThe following apps should be added to :applications in mix.exs:\n\n phoenix_html => phoenix_html is missing from hello\n cowboy => cowboy is missing from hello\n\nContinue anyway? Your release may not work as expected if these dependencies are required! [Yn]:\n<\/code><\/pre>\n
Development, test, docs and optional dependencies stay out<\/h3>\n
cowboy<\/code> and
phoenix_html<\/code> be out of my control and get required automatically by
phoenix<\/code>? That way I’d just need to worry about
phoenix<\/code> as a single, rich dependency.<\/p>\n
mix.exs<\/code> file<\/a>,
cowboy<\/code> is declared as
optional: true<\/code> and
phoenix_html<\/code> as
only: :test<\/code>,
gettext<\/code> likewise is
only: :test<\/code>. The reason these dependencies are declared is that if we eventually need them in our<\/strong> app, they must be at least compatible with the Phoenix version currently in use.<\/p>\n
exrm<\/code> and
phoenix_live_reload<\/code> can be kept out of the applications list. We don’t want
phoenix_live_reload<\/code> running in production, since there will be no live code reloading. As for
exrm<\/code>, it is a tool used exclusively to package our application, our business code has no need to know anything about it.<\/p>\n
phoenix_live_reload<\/code> works in development though? Well, when we start our application \u2014 with
mix phoenix.start<\/code>, for example \u2014 the code is available in the load path, but the
phoenix_live_reload<\/code> application is not started yet; it only starts when we connect to its channel<\/a>. We can try it out by starting up our little Phoenix app and checking which applications get loaded.<\/p>\n
iex -S mix phoenix.start\nErlang\/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]\n\n[info] Running Hello.Endpoint with Cowboy using http:\/\/localhost:4000\nInteractive Elixir (1.3.0) - press Ctrl+C to exit (type h() ENTER for help)\niex(1)> Application.loaded_applications\n[\n {:plug, 'A specification and conveniences for composable modules between web applications', '1.1.6'},\n {:hex, 'hex', '0.12.1'},\n ...\n]\n<\/code><\/pre>\n
Application.loaded_applications<\/code>. Now, after we make our first request by opening
http:\/\/localhost:4000<\/code> then calling
Application.loaded_applications<\/code> once again, we can see two new applications on that list:
fs<\/code> and
phoenix_live_reload<\/code>.<\/p>\n
iex(2)> Application.loaded_applications\n[\n {:plug, 'A specification and conveniences for composable modules between web applications', '1.1.6'},\n {:hex, 'hex', '0.12.1'},\n ...\n {:fs, 'VXZ FS Listener', '0.9.1'},\n ...\n {:phoenix_live_reload, 'Provides live-reload functionality for Phoenix', '1.0.5'}\n ...\n]\n<\/code><\/pre>\n
Declare your applications, even when you’re not using releases<\/h3>\n
dep<\/code> for that undeclared
application<\/code>, a runtime bug is potentially introduced, given we have no guarantee our new dependencies’ applications will get started properly.<\/p>\n
As a library author, you should take extra care<\/h3>\n
A<\/code> depends on
B<\/code>, which in turn depends on
C<\/code>, the application responsible for ensuring
C<\/code> is started is
B<\/code>, unless
C<\/code> is an optional dependency. In that case, the author of
B<\/code> should document the pluggable nature of that optional dependency appropriately, or even provide code generators if applicable \u2014 as Phoenix does with
cowboy<\/code>.<\/p>\n
Elixir tooling is our friend<\/h3>\n
app.tree<\/code> and
deps.tree<\/code>. They are priceless timesavers in this routine task of keeping up with our dependencies.<\/p>\n
Summing up<\/h3>\n
deps<\/code> function in our Mixfile:<\/p>\n
\n
deps<\/code> if it is a direct<\/strong> dependency.<\/li>\n
only: [...]<\/code> option if not every environment needs it.<\/li>\n
optional: true<\/code>. It is always good to document and explain how they are supposed to be included.<\/li>\n<\/ul>\n
applications<\/code> key in our
application<\/code> function:<\/p>\n
\n
applications<\/code> if it is required at runtime<\/strong> in production<\/strong>.<\/li>\n<\/ul>\n
mix.exs<\/code><\/strong>. Is there anything important missing? Any unused dependency? Then you can start taking a closer look at your external dependencies’ Mixfiles. Are all runtime dependencies properly required? What about their environments? Once you’re there, take your time to contribute, give back. These tiny bits might seem worthless at first, but rest assured, as some clever man used to say, \u201csuccess is in the details\u201d.<\/p>\n
\n