We’re continuing to look at the state of concurrency in programming languages and identifying what’s wrong with it. As a reminder, in Message Passing Is Shared Mutable State I argued that Go’s channels are shared mutable state with extra steps. But you may think Go’s channels aren’t true message passing. The honor for “best case for message passing” probably falls to Erlang. So let’s look at Erlang.
It is interesting, but some things are not as bad as they make it out in my opinion. It’s a bit too hyperbolic for my taste.
Forget to set a timeout on a gen_server:call? You get a potential deadlock that hides until production load triggers it.
That is not true since there is a default timeout of 5 seconds. The example provided would simply ”unlock” itself after that time:
%% Server A handles a request by calling Server B
handle_call(request, _From, State) ->
Result = gen_server:call(server_b, sub_request),
{reply, Result, State}.
%% Server B handles a request by calling Server A
handle_call(sub_request, _From, State) ->
Result = gen_server:call(server_a, request),
{reply, Result, State}.
I agree about Erlang getting it right by having the right primitives and sensible defaults, but providing riskier escape hatches when needed. Even though the style of the writing is a bit sensational (is it written by a human? Who can tell these days?).
I know what you mean. My main takeaway is that this is why you need to understand the idiomatic conventions - and the rationale for them - as well as the syntax of any given language. To put it another way, if you consider a language as if it were a map, there will always be areas that are labelled “here be dragons”. And that IMO is fine because, as software developers, knowing that is our job.
The pure model says “send a message, wait for a reply.” Each reader waits in line, the mailbox becomes a funnel, and throughput collapses.
is quite true in the “pure model”, but common tricks (which lines up with what the article says earlier lie with the programmer to know indeed), such as saving the from in the gen_server state, and spawning worker processes to perform async processing, where a gen_server becomes more of a scheduler itsself, which prevents bottlenecking but keeps blocking say for resource queuing. I’d say I try to reach for this particular “escape hatch” more than ETS.
Again, a bit of hyperbole in the article in my opinion.
You can spawn two process and suddenly have twice the output! Erlang gives you very nice building blocks to structure systems with, which I think are much easier than many other tools in other languages I’ve used.
But what paradigm or language is designed in a way that bottlenecks are impossible to create? I can’t think of one.
The thing is that Erlang isn’t really a conchurrency model, it’s a distributed programming model. In a distributed system you are GOING to have isolated subsystems communicating by messages that may not arrive. Like it or not. The message I read just before this was about a server down for maintenance and people being unhappy and surprised. At scale,there simply isn’t any other game in town.
I think the article fails in a circular way: if the premise is that message passing is ultimately shared mutable state (which is true), then it should be acceptable to have other forms of shared mutable state, such as ETS and persistent term?
Therefore the more interesting question is not what is and what isn’t shared mutable state, but what properties each of these offer. The article covers message passing well but it is wrong about everything else:
They are shared state outside the process model, accessible concurrently by any process, with no mailbox serialization, no message copying, no ownership semantics.
While ETS can be accessed concurrently with no serialization (that’s the whole point), data is still copied and it has clear ownership semantics. protected and private ETS tables belong to the writer process and will terminate when the process crashes, so they remove serialization while preserving isolation. Alternatively, persistent_term uses references with well-documented semantics too.
Furthermore, as @nzok mentioned, the article misses the biggest point: isolation is also essential for fault-tolerance and distribution. Erlang’s model isn’t claiming to eliminate all shared-state bugs. It’s claiming to give you the best starting point, strong defaults with well-designed options for concurrency, fault-tolerance, and distribution simultaneously.
That article looks 100% AI generated. I guess 2026 is going to be the year where you cant trust anything that does not come with a full replication suite, if the AI hype train is good for anything its devaluing all the trust me brah I wrote a blog people.