Documenting the use of :included_applications

I have been struggling with trying to get an application deployed to a staging/production server today. I say struggle, because it still doesn’t work and as smooth as the developer experience is in Elixir, so bumpy is the road to deployment.

In this young environment I can understand how the tools aren’t quite there yet or in heavy flux with all kinds of versioning and compatibility conflicts. I can deal with those. But one thing I encountered was genuinely baffling, and that is that the code you develop appears to be not the same as the code you deploy, especially regarding dependencies. Things that compile and run without problems in development fail to run in production, mainly because of mix configuration and dependency issues.

One thing that cost me a sizeable part of my day was the use of non-application external libraries. After juggling lots of deps in my (umbrella) application, it compiles without issue and gets released to staging. Only at runtime the application fails because it misses those modules. I found out that adding them to :included_applications is needed to make this work. This seems like such a basic thing I was surprised that the documentation makes no mention of this. This leads me to believe I am missing something maybe? Finally, the production app I have now built and deployed is not behaving the same as the application I built in development, but I haven’t quite figured out what causes this.

In summary, if I am right about those external libraries and :included_applications, I think we should have this in the docs front and center. If I’m wrong, can someone please set me straight? Stepping back a bit, I think the development and production code should behave more similarly. Things that compile/run in development should also compile/run in release. In my experience, right now they don’t.

Sorry for the rant, everyone, thank you for your time.

1 Like

I believe there is a chance you wanted to add the library to your list of :applications and not :included_applications. The latter is used only in special circumstances where you depend on it but you don’t want it to start because you will do it yourself.

The general rule is that you should always add a dependency to your :applications list, except the ones you only use during development and test. here is an article on the matter: http://blog.plataformatec.com.br/2016/07/understanding-deps-and-applications-in-your-mixfile/

The intent of :included_applications is for starting the supervision tree of other applications in the supervision tree of that application. This might be required because the parent application is tightly coupled to the included application or the failure handling is best when then included application sits at a particular point in the parent’s supervision tree.

When creating the start order of applications some tools will assume that once the parent application is started then the :included_applications are also started. This might be because another application has the included application in its :applications and so needs it to be started afterwards and will assume the included application is started. Also it means that tools may not allow two applications to include the same application because the application’s supervision tree would exist multiple times.

I initially tried adding it all to :applications, but when I add :rackla (git) starting my app gives the error below. Based on your reply, I have now determined Rackla is the only one that suffers from this (the other 3 can be added). So perhaps this is an issue I should log over there. Still, the issue remains that in development adding them to the dependencies suffices, while in release they need to be in :applications and this error appears only at runtime. Specially this happened to me with :plug_jwt (git).

** (Mix) Could not start application rackla: exited in: Rackla.Application.start(:normal, [])
** (EXIT) an exception was raised:
    ** (UndefinedFunctionError) function Rackla.Application.start/2 is undefined (module Rackla.Application is not available)
        Rackla.Application.start(:normal, [])
        (kernel) application_master.erl:273: :application_master.start_it_old/4

Yes, this should be filled as bug in the Rackla application. They should
not list a callback module if they don’t implement those callbacks.

Regarding the errors, yes, they only happen at runtime and in production
because the applications list is a list of runtime applications needed in
production. Although some tools warn of they see you are missing some
dependencies.

1 Like

So if I read you right, the applications list does double duty as an application starter and a production dependency manager. This is confusing to me, especially given the use of the only: :dev option in the dependency config. Anyway, I have it all working now, so many thanks for your help. Onward!

It’s not quite like that. Listing applications in :applications ensures that the application is loaded (is a valid app and is present) and the application is started (application can be started even if they dont have supervision trees). Starting the application without a supervision tree means future applications don’t need to check the application is valid/loaded/started. So runtime dependency management and application starter duty are the same thing in OTP.

Yes, another way to think about it is that the “dependency” guarantees the source code is in your disk. The application guarantees the code will be loaded, started and bundled inside a release.

Semantically and DRY-wise II find this a bit hard to swallow, but now I understand it I can work with it. Final question, what does the only: :dev flag do?

@beno it won’t fetch a dependency when the current project is used as a dependency or when you are running it only in production (see mix deps.get --only prod for example).

1 Like