Obviously it depends on what you’re building and you can craft your own supervision structures if you want to. But most of the time you can get the benefits of BEAM resiliency without having to drop down into the details.
Obviously it depends on what you’re building and you can craft your own supervision structures if you want to. But most of the time you can get the benefits of BEAM resiliency without having to drop down into the details.
Its a common pitfall for more inexperienced devs and I think its just a symptom of the language conventions.
Everyone is taught genservers with a client-side API existing inside the same module as handle_ functions and it blurs the distiction some.
I think if the convention was to create a separate module to implement the client-side convenience functions then the client-server split would be more obvious.
I don't believe the programming language matters much. What matters is one's familiarity with it. There are good tools and frameworks available for just about any moderately popular language. Every language has caveats. Every language has lots of pros and cons and when you average them out, they're essentially all the same.
Just a matter of personal preferences and habits.
It's my assumption that needless process-starting is from classic OO developers trying to get something closer to the objects they're used to. I'm not sure separating the client and API logic in tutorials would necessarily stop them from doing this. People who are hellbent on shoehorning familiar idioms into an unfamiliar language are going to do so regardless. But I see what you're saying.
I think it's more important to drill home that modules and processes have nothing to do with each other. I still catch myself conflating them once in a while three years in and it's caused the most confusion while trying to teach friends (although I'm not a particularly good teacher).
Both simple config and GenServers have their place in libraries. But if a library doesn't expose the fundamentals it gets gnarly quickly.
For instance, take Mint (https://github.com/elixir-mint/mint):
> Mint is different from most Erlang and Elixir HTTP clients because it provides a process-less architecture.
Mint is a low-level library which doesn't make attempt to manage processes (including HTTP pooling).
In contrast, Finch (which builds on top of Mint) includes pool management:
https://github.com/elixir-mint/mint#connection-management-an...
It can take someone a bit off guard when they realise that the library they use provide a "default pool" they were not aware of, and that it can become a bottleneck etc.
It was originally implemented as a single GenServer. When you app started up it'd read the filesystem and create a map of svg name => svg contents in a process. Then each lookup call would send a message to that process and get back the contents.
This has had the flaw of making template rendering single threaded. It's far worse than a file read per lookup
Hence, a library which contains a service is likely to have their own internal supervision tree. Your code can then sort-of latch onto this tree and piggyback on its existence. This is very common in web applications because when a new connection is opened, you spawn a process to handle the connection. If connections are (de-)multiplexed into channels like in HTTP/2 and HTTP/3, you'd just move up one layer and spawn a process per channel.
Likewise, if you have a limited resource such as Postgres connections, they are usually maintained in a pool, and access is queued until a connection from the pool is ready for use. This can circumvent the need for something like pg_bouncer in some use-cases.