How would PragDave separate this code?

The style might not have changed much within BEAM languages (it actually has, but more on that later), but I feel that this style is mostly unknown to non-BEAM languages. Since in those languages people typically don’t have lightweight processes, they usually resort to running multiple OS processes to split runtime activities. So for example, to start an external process registry, we would have a registry as an external OS process (e.g. etcd). To start/stop it, we would use some CLI API, probably in combination with an external lifecycle manager, such as systemd. Then we’d use some internal API (e.g. REST, possibly wrapped with some library functions) to interact with the running component. Such approach is in my view close to what you seem to be suggesting.

After using that approach myself for many years, and then also using the “BEAM way” for quite some time too, I empirically came to conclusion that I prefer the latter. I find it simpler, and at the same time more powerful and flexible. YMMV of course :slight_smile:

In addition, it’s worth remembering that child_spec/1 thing is Elixir specific, and it’s in fact a fairly new addition to Elixir. FWIW, I think it’s one of the more interesting changes introduced by Elixir, because it makes things closer to the way I personally think about code. With child_spec & support by Elixir Supervisor, a component/service can fully encapsulate the details of how its started, which makes things simpler for the user. Stick Foo or {Foo, arg} where you want it in the supervision tree, and you can now interact with Foo using the functions from the same module (or “submodules”). To me that seems very straightforward. Again, YMMV :slight_smile:

Well, you said earlier in this thread that it’s important that we’re discussing it, so here I am, discussing it :slight_smile:

4 Likes

Not all all.

I’m suggesting separating lifecycle concerns from functional concerns within the same codebase, that’s all.

Right, and this is what I believe the whole industry has been doing for decades, and it’s still doing it now with the mainstream microservices approach. Thus, I actually think that the Erlang way, despite its age, is in fact avangarde, and the Elixir way with childspecs is even more progressive.

4 Likes

Fantastic!

Carry on, everyone. Nothing to see here.

So, just to see if I understand both points of view clearly:

@pragdave defends we should split functional concerns from lifecycle concerns. In his opinion, such can be achieved by separating the start_link, init and callback functions from GenServer into a separate file, while keeping the API that is actually used, in another file.

@sasajuric defends that both lifecycle and functional concerns belong together in the same file, because you need both to make effective use of an API. It is not clear to me if @sasajuric likes the micro-services approach we have seen recently, but it looks like he prefers the way Elixit’s community is dealing with the way APIs are organized anyway.

I apologize if I have miss interpreted any comments, but I feel this is a succinct summary of both points of view.

Perhaps the former emphasizes the client point of view (related to interface segregation); the latter emphasizes the maintenance point of view - being able to see everything the process needs to function in one place to make it easier to reason about the whole - up to a point. When the capabilities of the process grow large enough it is time to push those into separate modules (or perhaps even separate processes), in effect composing the capabilities to realize the process.

3 Likes

Roughly speaking yeah. My position is that lifecycle functions (childspec and optional start_link) are a part of the component/service API. I’m not saying this should always be in the same file though. I do concede that lifecycle can be thought of as a separate API concern, so in the case of a larger API, we might decide to move lifecycle and some other API concerns into separate modules (in which case I’d try to keep lifecycle in the top-level module). In this particular example, I feel that the module is too small, and IMO splitting it causes more confusion with little to no tangible benefits.

I feel that the mainstream microservices approach (i.e. the approach based on one OS process per microservice) is an improvisation around deficiencies in the first chosen technology (the language of choice + runtime). I’m happy to discuss this in further details, but it’s probably better done in another thread.

5 Likes