jola

jola

Let libraries be libraries

I’ve been bumping into some rough edges recently with Elixir dependencies that use app config and start their own supervision trees and I want to petition the community to move towards better library design

Most Liked

hauleth

hauleth

That is not true. You can 100% connect to different DBs using the same module. It is not common practice, because that is not the common problem, but it is fully supported and documented.

MyApp.Repo.start_link(
  name: :some_client,
  hostname: "client.example.com",
  username: "...",
  password: "...",
  pool_size: 1
)

It is not fully ergonomic, I agree, but at the same time that is not a common situation when you need such thing.


About the topic, I mostly agree with @jola. There are however grey areas sometimes. If you use Application just for env, then yeah, it is dumb, but there are situations when there is no point in having multiple instances of the processes provided by the library. Like in my systemd library, where there is no point in having multiple instances, and moving that to the user-side to manage processes for this would only make it worse wrt. UX.

Another thing is that sometimes the process running in the background should be transparent to the user. Like in my aww library. Some may argue, that cache there is a wrong approach and user should cache on their own. But at the same time, when you are using (most of the time indirectly) Erlang’s DNS resolver do you think that there is a cache for A and AAAA responses? Because there is one.

Another thing that uses their own supervision tree, but people do not think about it at all, is Telemetry. Yeah, really, there is supervision tree. To be fair, it would be impossible to implement without that tree (at least if we want to use ETS, after you fire :telemetry.persist() it is not needed anymore).

So I would say that it is a complex topic, but when in doubt - do not spawn your own supervision tree, you ain’t gonna need one probably.

mudasobwa

mudasobwa

Creator of Cure

Sometimes we want to take control over the supervision tree, sometimes we just expect the library to do its work once added to mix. Sometimes the configuration is an anti-pattern, in 99% of cases it’s not, specifically in modern environment when it’s all containerized. Also, starting an application is necessary if it needs to track the main application startup.

I finally came up to a compromise. I declare the application in my libraries, empty-handed by default. Then I got an option to have a config and I use it to start the supervision tree within the application.

cevado

cevado

A few years ago I wrote an article on that(and I just discovered that my abandoned blog is down bc the domain expired :person_facepalming:, i’ll send the link here if i’m able to get the old domain set or if i setup a new domain), I was way less polite and ranted a lot about the subject.

Supervision trees are not for DX, they’re for resiliency, DX is your libarary with good defaults so just a plain copy/paste of your docs resolve 90% of the needs someone have.

It feels really terrible when a library hijacks the power to define how your app starts and recovers, and supervision tree is about that, not about ease of use.

Again, needing just one instance of a supervision tree is not the issue, it’s aboult controlling how it gonna behave when it fails. If you just need one instance, make your childspec use a name scoped by your library, or allow it to optionally receive the name and default to getting the name from the application env that the user can set, the dev using it decides where it sits in their supervision tree, it doesn’t lose usability, it doesn’t hijack the control over what should bring the app down or not.

Not to the quoted people here, but people writing libraries need to remember once an application that your app depends doesn’t start it blocks the entire application of starting, I’ve had a lot of issues just because one particular library couldn’t start and it was impossible to start a new instance of the app no matter how I configured their library, I’d need to remove them of my dependencies to make it work.

For writing applications instead of libraries my rule of thumb is: can you assure there is no scenario where using your application as dependency will block the main app of starting? If you can’t assure that, create a worker and allow people to set their supervision tree appropriately to the place your library have in their systems.

Where Next?

Popular in Blog Posts Top

rocket4ce
A comprehensive guide for deploying Phoenix 1.8 applications using Coolify on Hetzner servers. Covers server setup, Coolify configuration...
New
AstonJ
I think <span class="hashtag-icon-placeholder"></span>liveview is going to be a big topic in the foreseeable future, so wondering whether...
New
T0ha666
In this article, we discuss how to create a Docker image for a Phoenix application. https://t0ha.ru/en/devops/elixir/2023/11/10/how-to-b...
New
ryanrborn
A long-running GenServer holds orders, positions, strategy state, and operational flags for a system trading real capital. The post walks...
New
ErlangSolutions
An infographic that compares Erlang, Elixir, and Go’s strengths in the respect to the programming languages’ concurrency, reliability, sc...
New
aymanosman
The desire to produce structured logs is common. In this article, I will survey the major approaches one could take to achieve this goal ...
New
marcin
Hi! :waving_hand: I wanted to refresh my knowledge on how to mix phx.gen.auth with local password users db, as well as OAuth providers s...
New
brainlid
Jason Stiebs shows a couple ways for a LiveView to make it easy for users to click and copy an important value to their clipboard. He sho...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127089 1222
New
brainlid
Dialyzer is a tool that you’ve probably heard about in the Elixir community. You may have even used it. However, adding Dialyzer to an ex...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 43487 311
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement