Why are children not given to the start argument in `mix.exs`?

The application/0 definition in a typical mix.exs file has an essential shape like this:

def application() do
  [mod: {MyApp, []}]
end

… where a tuple is given to the key :mod containing the application callback module and the start argument.

No idea why, but the thought occurred to me, why are an Application’s children usually defined in the Application module itself? In this example, for instance:

defmodule MyApp do
  use Application

  def start(_type, _init_arg) do
    children = [{Cluster.Supervisor, [topologies, [name: MyApp.Cluster]]}]]}]
    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

… rather than passed to start/2 by the mix.exs file? So, I’ve spent the last several years thinking this was just a convention, except that it seems like libcluster won’t automatically connect nodes if I rely on mix.exs in this way. In a bare application, this structure:

def application do
  topologies = Application.get_env(:libcluster, :topologies) || []

  [
    mod: {
      MyOtherApp,
      [children: [{Cluster.Supervisor, [topologies, [name: MyApp.Cluster]]}]]
    }
  ]
end

… and …

defmodule MyApp do
  use Application

  def start(_type, init_arg) do
    Supervisor.start_link(init_arg[:children], strategy: :one_for_one)
  end
end

… which I would’ve assumed was equivalent, doesn’t work. So there’s definitely a difference here that I’m not catching, but I have no idea what it might be.

I guess the root reason is the config.


mix.exs is read at compile time, so:

def application do
  # will be determined at compile time.
  topologies = Application.get_env(:libcluster, :topologies) || [] 

  # ...
end

If you put libcluster config in config/config.exs:

# config/config.exs
config :libcluster,
  topologies: [
    # ...
  ] 

Then, it should work as you expected.

But, if you put libcluster config in config/runtime.exs:

# config/runtime.exs
config :libcluster,
  topologies: [
    # ...
  ] 

Bad things happen.


You can first try to check which config file your relevant configurations are located in. (Related docs - Mix — Mix v1.14.5)


EDIT: But, even if it works as you expected, you shouldn’t do that. Checkout what @benwilson512 said.

1 Like

I think you’re on the right track, but the other thing to emphasize is that really anything you’d want to do at runtime to configure children, or choose which children to run, won’t work properly in mix.exs. It’s also important to note that mix.exs is run prior to any dependencies being compiled because it’s the thing that defines the dependencies. So if you want to do any child spec that needs your dependencies, that’s out too.

2 Likes

Makes a ton of sense! Thanks so much!