Iâm the primary author of the FarmbotOS Nerves project. Iâm still reading thru this entire thread, but i also agree that Elixir has issues with configuration. I had always that was inherited from Erlang, since it suffers many of the same problems.
The following are my opinions on what Iâve read so far as an end user of the Mix/config system in varying use cases. I apologize in advance if it is a little jargony feel free to ask and i will try to clarify anything. Iâve separated it this way to make a point. Right now the current Mix.Config
system is applicable to all of these very different deployment strategies, and being so I do not believe there will be a system that 100% envelops all of them.
Building a Nerves based project
This is one of the situations i spend most of my time, and it may be a bit long winded.
As the original post described Iâve had to do some weird things as to suit application requirements. One example of this is retrieving things
in Mix.Project.config()
such as:
- application version
- application environment (
dev
, prod
, test
etc)
- nerves target (
host
, rpi3
, my_custom_hardware
, etc)
Now since those are in fact compile time things, it might look weird that i have a separate module tracking their values, this comes back to a shortcoming in Nerves currently - we donât have a code reloader yet, so during development, i frequently do something a little scary that i wont go into detail here. The gist of it is that if i recompile a module while working on a nerves device, Mix is not available, so those fields are also not available.
The other thing that i would like to point out is that in my Nerves based projects i often find the need to not only retrieve config at runtime but also set it. For the most part i use something like Ecto/Sqlite for this, but i do have a bad habit of storing data globally in Application.put_env
and getting it with Application.get_env
. A tangible example of this is interfacing with hardware. Elixir lends itself to behaviours for hardware nicely. In the Farmbot project we interface with a UART device. itâs not always possible to have the device plugged in during development so doing something along the lines of Application.get_env(:farmbot, :firmware_handler)
works really well. I can listen for the device being plugged in, replace that value with the Firmware.UARTHandler
module and restart the firmware stack. When itâs unplugged, replace the value with Firmware.StubHandler
. You get the idea. Now is reconfiguration at runtime something that Mix/this new thing needs to handle or even be aware of? i donât know to be honest, but this does feel hacky to me anyway.
Those are the two main experiences i have with configuration of Nerves projects.
Building a library specifically targeting Nerves projects
This section encapsulates projects such as nerves_network
, nerves_init_gadget
and the like. This is usually where i experience the most pain and it is not particularly the fault of Mix.Config, but it does allude to a big picture issue i have with Elixir/Erlang libraries. There are to many ways to configure an application, and end users of the library often expect to be able to configure how they see fit. Letâs take nerves_network
as an example.
you can configure Nerves network in two different ways.
use Mix.Config
config :nerves_network, :default, [
eth0: settings
]
or you can do
iex()> Nerves.Network.setup(:eth0, settings)
This is a nightmare handle as a library developer.
Building a general use Elixir library
The Nerves specific issue i denoted above applies here, but Iâve found it is less of an issue for generic libs. One issue i have with this use case is that lib developers frequently use sub dependencies. If those sub dependencies need configuration, it is forced upon the end user of your library to configure them.
Building a generic Elixir application
To me this is where the current system shines in my opinion. I donât have many complaints with the caveat that i donât usually âdeployâ these applications. (so no distillery, or similar).
Building a Phoenix based project
I donât have the most experience with Phoenix, but I do work on two production apps. One deployed on Heroku, one deployed on Gigalixir. Both share a common issue of using environment variables to replace the things in Mix.Config
.
Closing thoughts
After rereading this post it seems like iâm just crying and i donât really have any offered solutions or ideas. Truth be told i donât actually know what my ideal api would be for each of the listed use cases and i donât actually believe there is one all encompassing api that would handle all of them 100%. There is certainly going to be trade offs and balances to be struck. Iâm afraid that changing the current system will only fragment the issue rather than push toward a good solution for everyone.