"More than one thing at a time"
2020-06-17Underjord is a tiny, wholesome team doing Elixir consulting and contract work. If you like the writing you should really try the code. See our services for more information.
On a recent Elixir Outlaws episode Chris Keathley told us all a nice story of the advantages of Elixir as opposed to Ruby. His frustration with Ruby and appreciation of how Elixir works resonate at the frequency of my own frustrations and joys. I believe my titular quote is accurate, that's one of the primary things he noted. How nice it is to use a runtime that can do more than one thing at a time.
And I've never really used Ruby. What I have used is a lot of Python. And some Node.js. And I find the way they execute, async or not, to be frustrating and firmly in the camp of mostly sufficient. Not aspiring to greatness.
I really like Python. I enjoy using it. I'm skilled enough with it. I can see why people like Node.js to some extent. I understand that Node.js allows you to stick to a single language and provides a bunch of other interesting possibilities. Some of them are unique to JS. But I find both lacking when I compare their technical underpinnings with the BEAM which Erlang and Elixir runs on.
Efficienct resource usage is an effective cost reduction
Chris mentioned how many instances running the ruby service they could drop and replace with much fewer Elixir instances. This is bound to end up saving Bleacher Report a bunch of money over time. That wasn't the point of it all but I think people should keep in mind that performance directly relates to cost. Especially in the cloud.
With auto-scaling cloud infrastructure the effects of strong utilization of resources can be very noticeable on the bill. Being competently concurrent and parallell means better utilization of the CPU's you are paying for. Providing better latency guarantees by means of the BEAM scheduler's pre-emptive scheduling as compared to single-threaded cooperative multi-tasking means that you reduce the risk of a single dodgy code-path or an odd piece of data processing blocking the event loop for a significant time.
The capabilities of your runtime sets limits on your potential
You can build great things in basically any language on any platform. And a lot of languages have distinct strengths that are not strictly technical but would take enormous effort to dislodge from the ecosystem where they were born. Such as Python and machine learning which is a matter of library support rather than technical fitness and it dives deep into C/C++ to extend Python in a performant way. Node.js has huge developer mindshare, a vast library ecosystem, near-native cross-platform app development that cannot be replicated without Javascript because of app ecosystem restrictions and it enjoys exclusive ties to frontend development.
Python won't unlock a stronger concurrency and parallellism story unless it manages to get rid of the GIL. So building highly performant web services in Python will always include paying attention to these limitations and working around them. That doesn't make it impossible or even particularly hard but I've worked with systems where the GIL was definitely a factor. Much as the single-threaded nature of Node would have been.
The BEAM VM, Erlang and OTP were built for creating reliable distributed systems. It does that well and has a strong core feature-set for that. The distributed computing story incidentally transfers well to online services such as web applications. The approach it uses with inexpensive processes and message passing also translates very well to multi-core workloads.
This is an incredible foundation to stand on. Elixir gave it a more approachable and dare I say modern language that people had an easier time getting going with. Erlang was already a great success for some companies but somewhat under the radar.
I firmly believe that this gives any appropriate application or system built on this foundation a much higher ceiling before the runtime and core building blocks actually start limiting progress.
When I see ideas about porting something born in the Elixir ecosystem, such as Phoenix Presence or LiveView to Node.js I usually scratch my head. I get why they'd want it but I don't believe in the idea. Both of those are examples of strengths of the BEAM, reliable stateful services in distributed applications. Data is maintained across the cluster, there is a strong approach for limiting damage (supervision trees, etc) if a process holding data breaks due to a bug, an outage or similar. You can clone the surface of LiveView or Presence and go "BAM! Node has it too". But I bet it doesn't.
Pay attention to the trade-offs (if you want to make something great)
This doesn't just hold true for Elixir. There are also plenty of places where Elixir and the BEAM would be the wrong choice. Consider React Native (or NativeScript, Capacitor, Cordova, Ionic, any cross-platform tool for making apps) for example. Most people are aware that they are making a trade-off when selecting React Native. And most people are probably right about picking it anyway. Because making native apps is expensive and time-consuming and you just want iOS+Android and they should be basically identical anyway.
By choosing the cross-platform tool you are probably binding yourself to a decent app. But you are limiting your potential to make it great. Technically you could drop into native implementations per platform with React Native. Just like a Python dev can always "just write some C". That's not normally why you'd choose the tool. You might even recruit based on the tool and not actually have anyone that know C, Swift or Kotlin that could dive deeper.
The best mobile apps are typically done natively in the intended tools. They fit in, they can integrate with all the relevant APIs of the operating system at the full level of detail without abstracting away the differences. They can take advantage of any optimizations the platform owner makes and they do not get screwed over by incompatibilities quite as often.
We see the same thing with Electron apps (and equivalents) on the desktop. There the problem is more directly felt because the overhead of running them is ridiculous compared to what the apps actually do. They all run a full browser. The amount of resources used by Slack, Discord and Spotify is maddening. I try not to think about how fast they could be, how lean. In Spotify's case, how fast, lean and useful it actually once was. And how much more efficiently they could do things.
So for mobile apps and desktop applications the potential for greatness is higher when working with the languages and tools that are well suite to the task. The same is true for web development. Stateful and distributed or even stateless thanks to the soft real-time latencies, I think Elixir and the BEAM have an incredible high ceiling for technical achievements compared to most comfortable high-level languages that it compares to. So consider what your language and ecosystem provides you and if it is in line with what you value. If that checks out, you are doing fine. If it doesn't maybe something to think about.
I grant that I'm biased towards Elixir, BEAM and OTP. But I do really like Python. I just have frustrations with it. I don't hate Node either, I'm just not sure I like it. If you want to follow my writing this blog has a completely normal RSS feed and you can sign up for my newsletter further down. If I'm very wrong or you have thoughts, questions or concerns feel free to get in touch via lars@underjord.io or just find me on Twitter as @lawik. It is not my intent to pee on your ecosystem, I just have feelings and opinions. I'm sure it's a fine ecosystem.
Underjord is a 4 people team doing Elixir consulting and contract work. If you like the writing you should really try the code. See our services for more information.
Note: Or try the videos on the YouTube channel.