Is Elixir the right tool for your job? Find the answer to that question from Sasa Juric, the author of “Elixir in Action”.
Listen to this episode on:
Is Elixir the right tool for your job? Find the answer to that question from Saša Jurić, author of Elixir in Action. In this interview, Erik Schön, author of The Art of Strategy and Managing Director of Erlang Solutions Nordics, joins Saša to explore the key benefits and practices of Elixir, a functional language with over 20k commits on GitHub.
This episode of the GOTO Book Club was made possible thanks to the support of GOTO Amsterdam. Created for developers, by developers, the GOTO event series goes beyond annual conferences held in Chicago, Amsterdam and Copenhagen: you can also join online confernces, online masterclasses and meetups with top creators and leaders in tech year-round.
And if joining one of these top-rated events isn't enough to satisfy your hunger for the latest in tech, you can watch over a thousand high-quality talks on the GOTO YouTube channel — subscribe now for ad-free videos released (almost) daily and join GOTO in person or online at any upcoming conferences using the promo code ‘bookclub’.
What is Elixir and how can it help with high availability?
Preben Thorø: If I would start, I would like to ask you Saša Jurić, what is Elixir?
Saša Jurić: That's a good question to start the conversation. So Elixir is a programming language. In theory, you could say that it's a general-purpose programming language, but in my personal view, it has its particular sweet spot of what kind of programs can you write with Elixir and such programs are what I call software systems. And by that, I mean, any kind of program that runs on the backend side of things, any kind of server-side program such as a web server but also, for example, say a database or a message queue.
What is particular for software systems compared to other types of programs is that once you put them into production, you start them for the very first time in production, they have to be running for a long period of time. Constantly, continuously for a long amount of time, like a couple of years, or maybe even a couple of decades. During this period, you don't really want them to go down because that essentially means that the system is not providing any service at all.
Another particular property of a software system is that at any point in time, it's doing a bunch of different things, a bunch of different activities are happening within the system. Like, all of us are making requests to a web server and each request by a different person has its own separate activity. But beyond just handling requests, the system has to do other things like CRM, background jobs or periodical jobs. It might need to manage some kind of an in-memory state, such as cache, for example. It might need to do some load controls such as applying back pressure or rate-limiting.
A lot of stuff is happening just beyond playing requests responding. What's interesting is that these activities are mostly mutually either very loosely dependent or in many cases, even completely independent. Like, your request and my requests are typical, you know, completely unrelated. What this means is that in the software system, the semantics of success are not binary. We have degrees of success, which is not true for every type of program. If you take say a compiler or typically any kind of a command-line tool, these programs get some inputs, they do some processing, they produce the output. But they can either succeed or fail. There is nothing in between, but for software systems, you have these degrees of success.
So, ideally, we want the system to always work for everyone but clearly, this is not possible. Because ultimately we're humans and we're going to produce some bugs and something will go wrong. Even if we are able to write perfect software, this software runs as some hardware, which will occasionally fail. Typically, in many systems, we depend on external stuff not developed by us. For example, an external database or third-party services, or payment gateway. So these things might fail and it's completely out of our control.
Things will go wrong but because there is a non-binary idea of success, we can implement; we can capitalize on this and implement the system in a way that it provides as much of the service as possible at any point in time. Even when something goes wrong, which is better than providing nothing. This is a very particular challenge for any kind of software system. For me, this is high availability. It's not about chasing some mythical amount of nights of uptime. It's about providing as much of the service as possible for our users.
And also, of course, the system should be able to automatically detect when something goes wrong and recover automatically from that failure as soon as possible, given the circumstances. So those are the challenges of the software system. And, again, they are really applied to any kind of software system, regardless of the particular business domain or even scale. Even in smaller systems and simple systems, you still want your system to be mostly up and running and providing as much of the service.
Saša Jurić: Elixir as a language gives us tools to address those challenges. Gives us the basic building blocks, very simple in their nature but very powerful and flexible to approach the challenge of high availability in a systematic fashion. I want to say that Elixir is not the only language available for this job. There are a couple of other languages, like, for example, Erlang, or List in Erlang, or Gleam, as an example of a viewer as a strongly typed language. What ties all these languages together is the fact that they share the same runtime, which is called BEAM.
This is the name of the Erlang virtual machine originally designed or written for the Erlang programming language. In time we had a bunch of other newer languages built on top of BEAM. BEAM is really the secret sauce here, which allows this story to help them, which allows us to build highly available systems.
Key takeaway no. 1
The key ingredient of Elixir's support for high availability is BEAM — the Erlang virtual machine.
How does BEAM compare with other virtual machines like the JVM (Java Virtual Machine)?
Erik Schön: So, Saša Jurić, speaking of the BEAM, Elixir’s virtual machine: how would you say that compares to other virtual machines that people might be more familiar with like the Java Virtual Machine, the JVM, for instance?
Saša Jurić: What I personally find about BEAM is that it really has a strong focus on what it wants to do. This is the power of the software systems highly available long-running programs which should ideally never fail — never go down completely. BEAM has been built from the ground up, from day one, even day minus one, if you will, before it didn't even exist. There was a lot of thought put into it: how to build such a runtime. So BEAM provides many things at runtime, which other runtimes typically do not.
Let me briefly explain how it works for BEAM so it's clearer. Essentially the way it works is like when you build your programming in a BEAM language, such as Elixir or Erlang or other languages, you will start your program as a single operating system: process starters. This is the instance of BEAM where our program is running. If I run five BEAM programs, I'm going to have five of those instances, five of those processes. Now, within a single BEAM instance, we can start many small lightweight independent programs, which we call processes.
So just to clarify, the process is not an enormous process for hundreds of thousands, millions up to, I think, around 100 million processes per single BEAM instance. These processes — these programs — are completely isolated from each other. They share no memory at all. They have their own separate memory space and they can only communicate through sending themselves messages, which is also known as message passing concurrency. They are completely isolated from each other.
If a single process crashes, the single small program crashes. All the other processes in the system are still up and running, so they will not fail. This crash can be detected. And these are like the basic foundational stuff that we get at the BEAM level. What this means is that we have support for microservices, at least some parts of microservices directly at the random layer and therefore directly at the language level, we can do stuff for which in other languages, you have to fall back to the OS level and run a bunch of different OS processes and different components. We can orchestrate them through some service manager and what not. You can do a lot of that stuff directly in a BEAM language such as Elixir.
So the idea of building fault tolerance in the highly available system is in its basic form; it's very simple to me. Let's say a system has to do this huge chunk of a job. Everything the system has to do, it's like some huge big square. If you take this big square and somehow strategically split it into a small number of independent or loosely independent programs, then suddenly no one is too big to fail. If you experience some sort of an error in a particular part of the system, like maybe there is some division by zero square with a minus one, something unexpected happens. Still, most of the system is up and running, and we are still providing as much of the service as possible.
At the same time, because failure and, in general, process termination is not a silent event, other processes can be notified about it. You can implement self-healing strategies. When one process crashes, the other one gets notified about it and starts a new process in its place, or maybe redirects some jobs to another process that is still available. It's a pretty simple but very powerful idea compared to other virtual machines. Well, I didn't do a thorough comparison but, in general, as far as I know, no other runtime layer has such support or focus for highly available systems.
In particular, for example, in JVM, as far as I know, there is no life with concurrency at the runtime level. People do this on top of JVM. The most notable example is AWK, which takes a lot of ideas from Erlang and brings also a lot of other interesting ideas to the table but this is implemented at the library level. So you implement that kind of language. I don't know which languages it's written, probably Java. But what this means is that you kind of fall short, you basically can do only what your runtime can do.
For example, in BEAM, these processes are scheduled preemptively, so to speak. When a single process runs in an infinite CPU bound loop, it's moved out, then someone else gets the slot. You can terminate the process even if it gets stuck completely, even if it refuses to stop. As far as I know, these things are not possible in AWK or anything else because you essentially don't have the support from the runtime or the runtime doesn't know about these lightweight concurrencies. In my view, BEAM is very focused and very well built. It's a really stable runtime. It has been around for more than 20 years or so.
Erlang itself is old, I think about 30 years or maybe even more if you take the design phase into account. So a very stable, proven practice in large systems, in diverse systems, such as WhatsApp, for example, and, of course, in telecom systems. So it's the best option we have available today, or the most suitable option that we have available today as a foundation for building software systems, or again, highly available fault tolerance and scalable programs.
Key takeaway no. 2
BEAM is very stable, which has been proven in practice in very large systems (like WhatsApp).
Can you compare Golang or DarkLang and the BEAM languages?
Erik Schön: I really agree with you, Saša. I'm thinking in the community now there's a lot of talk about Golang and Dark Lang, which are in some aspects similar. What are your thoughts on the differences and similarities between these languages and the languages on the BEAM? Elixir being one of them, of course.
Saša Jurić: Yes, so let's start with Go. In my view, Go is a great language for building tools — the complete opposite of software systems. This is true especially if you want to distribute the message to a large audience, which runs in a bunch of different operating systems and what not. Go really has a great deployment story, which I really like. We build this standalone binary and give it the way and it works. So this is super great. I think, as far as I know, it's probably the state of the art in that particular area. It's a very simple language to pick up.
So those are pretty good things. I know that people are building systems with Go but personally, I don't feel that Go is as good of a fit for that job as BEAM languages. I mean, Go does have a life with concurrency but that's pretty much all it has. It's not just about having lightweight concurrency. It has to be designed in a particular way if you want to build software systems with it. Go, for example, all these Goroutines are sharing memory, which can lead to all sorts of strange problems and bugs. If a Goroutine crashes, then the entire program crashes.
This is how I think about this: if you have a single Go program, which runs maybe a million coroutines, you are adding all kinds of WebSocket connections to some real-time game server or something like that. And if there is a single bug, somewhere square root of minus one, a single Goroutine crashes, all of these connections go down and this is like super disruptive for all the users. This is the kind of thing that will not happen in BEAM when you design it properly. You will just have one process crashing.
Then Go also basically still has co-operative scheduling. They did a lot of improvements there but as far as I'm aware, they're still not completely preemptive. You can still end up with a coroutine which runs in some longer CPU bound loop and just takes up a single scheduler. A couple such routines basically block your entire system. Also, you cannot really terminate the coroutines. This is a very interesting thing that you have on BEAM. So in BEAM, because processes are runtime entities, you can terminate it by the runtime. You can ask the runtime, "Please stop this thing now." And no matter what it does, it's going to be stopped now because this is the runtime level service.
So this is the first class cancellation at the runtime layer. Those kinds of things pretty much are not possible to implement reliably or completely reliably in Go or in, say, on top of JVM again because the runtime doesn't have the support for that. Just maybe as a passing mention, I explained this in more detail for a demo-driven presentation about a year ago when I was talking at GOTO Chicago, giving a talk called "The Soul of Erlang And Elixir."
I'm giving a very high-level overview here but if you want more insights you may want to check out that talk. So, in general, I think that Go, for me personally, I will definitely use it and recommend it to build tools, to build one-off programs. It works pretty great for that. Probably the best in class for many such scenarios, but not for a software system. Again, I understand that people are building large and interesting things with that. But personally, I don’t think that will not be the sharpest way to do it. I feel that BEAM languages are a better option.
Now, when it comes to Dark, this is a very interesting story. I really like what I've seen with Dark. I didn't really get to try it but I just saw a couple of presentations. I think that Dark really has a very important story. And this is a story that Dark starts with: the premise that backend development is very complex because you have to use a huge amount of different tooling and somehow glue it all together. This is like the modern state of the art. You start with Kubernetes and then run a bunch of different components and you split your system to a bunch of microservices and what not.
The amount of technical complexity we introduce here is crazy. Server-side backend side programming is way too complex these days. Dark aims to solve this by being like a single tool. You learn this one tool and you can do everything with Dark. It reduces a huge amount of this technical complexity. I really like this story. Now, what's interesting is that you can sort of get a similar story on top of BEAM languages. Precisely, because BEAM already at the runtime level gives you a lot of the things that you get otherwise from the operating system. So, we sometimes say that Erlang or Elixir is like an operating system for our code.
When you have operating systems services in your code, then you don't have to fall back to the operating system level, and you can do a lot of stuff from the language. I've had a bunch of these examples in practice where I built a system using exclusively Erlang or using exclusively BEAM with nothing else running on the side supporting it. So, no engine next, no external in-memory KV, because we have in-memory key-value storage called Erlang Term Storage or ETS.
With other languages, I will have to fall back and run a couple of different processes and a couple of different third-party components and glue them. And again, this is a huge amount of technical complexity. With Erlang, at least you can frequently get away just fine with a single project and a single OS process running through each machine in the cluster. I believe that, basically, with BEAM, you can get similar sort of properties that Dark tends to give you. However, BEAM and the entire ecosystem is kind of a ground-up story. It's more like a toolkit, less than a framework.
So, with BEAM, the library is overlaying in Elixir, you get relatively low-level obstructions. Libraries give you some medium level of obstructions. Dark sort of starts from the opposite direction and has super high-level obstructions. We have good foundations in place. And again, in my view, you can typically implement something like that on top of BEAM and not many other platforms. But we are liking this high level of abstractions.
I would like to see the ecosystem evolving to the point where, when I say, "I want to build a small to medium distributed web-facing system" — and in 15 minutes, we're bringing in a couple of libraries, a single project, and then it just works. This is perfectly possible. I would really like to see our record system getting there.
Key takeaway no. 3
BEAM languages handle a lot of the complexity for doing serverside programming. You could say they are the operating system for our code
Thoughts on static typing and the BEAM languages?
Erik Schön: Yes. I think that's a very good comment. I think that the ecosystem around Elixir and the BEAM are fantastic, and I'm sure there are brains as we speak thinking and working on exactly those things. So a slightly related topic, I was listening to and watching another episode on Elm in Action. Elm is a totally different beast, of course. But what I found interesting in there is the approach taken to static typing. Which is very, very rigid. What are your thoughts on static typing for the BEAM and BEAM languages?
Saša Jurić: In my view, the lack of static typing is the biggest deficiency in BEAM languages. I know that there are like camps dynamic versus static. I've personally done both for many, many years. For the past 15 years, I've been mostly using dynamic languages. I'm now pretty much certain that I believe that static is definitely a better option. I still love Erlang and Elixir. These are like my first languages, regardless of the lack of static typing, because they offer something that I cannot find anywhere else, but I would love to see static typing stories on BEAM languages.
Unfortunately, mostly, it hasn't been available. So what we have for Erlang and Elixir is what is called the success typing through the tool called Dialyzer, which I like to say is far from perfect but it's the best we got. However, there are initiatives, very interesting initiatives. There's one language called Alpaca and another language called Gleam, which looked very, very promising. They basically are aimed at bringing static typing into the BEAM world, like, proper on static typing. This is something I'm super excited about. I still haven't had the chance to try them out but I would definitely love to see how this story unfolds.
One challenge with BEAM languages is because you really have two dimensions in those languages, there is a functional dimension. Erlang is a functional language and Elixir is a functional language. Typically, all BEAM languages are functional because somehow the runtime itself is tuned to that. But then there is a whole other dimension, which is a concurrent dimension. That's what we talked about, the ability to run and manage a large number of programs within a single OS processing. This is usually lacking in other languages.
These two dimensions serve completely different purposes. And the challenge is I'm curious to see how this will be solved in Gleam, for example, is how to get type-safe message processing. This is going to be very interesting to see how it unfolds but in any case, I'm certain that strong typing can be added to BEAM languages. I'm very, very excited about Gleam, and I'm looking forward to seeing how it unfolds.
Key takeaway no. 4
The lack of static typing is the biggest deficiency on BEAM languages, but it will likely be solved with the emergence of Gleam and Alpaca
From a business perspective, what are the key benefits of Elixir?
Erik Schön: Yes. I fully agree with you. I think that's a very interesting story, and there are even rumors that WhatsApp (they're being a big user of all of the BEAM languages called the Erlang) are very much into strong typing, and they even invented different new versions of languages, including strong typing. So let's see how that plays out. I'm really excited about it. So moving a bit into Elixir, what would you say are the key benefits from a business perspective? Let's say a business user considering Elixir, what would be the key benefits from that type of thing?
Saša Jurić: As I said, Elixir, like any other BEAM language for me, should be strongly considered when you want to build a service side system. I don't want to repeat all that again but definitely give it a try, at least give it an evaluation. But, of course, it begs the question, why would we choose, say, Elixir or Erlang, which is the first BEAM language. And clearly the whole runtime has been designed for Erlang. There are, of course, other languages as well. So in my personal view, and just to be clear, I'm not a member of the core team, or I'm not the creator of the language or anything. So I'm just a user.
So I'm a happy user of Erlang and Elixir. My personal impression is that what Elixir brings to the table compared to Erlang is a better approachability and maybe a better developer productivity story. I have been using Erlang in production for a couple of years before Elixir even existed. Then through the middle of the past decade, I was using both languages side by side. Finally, a couple of years ago, I moved completely to Elixir. I liked the thing that I had, like, a good, you know, practice with both languages. So I could see the pros and cons.
In my view, Erlang is a very simple language. People get confused. People think that Elixir is simpler but that's not true. Erlang is a simple language. It just looks strange to most people because it has this Prolog-like syntax but like very, very simple language. That's really cool. Very simple syntax and very regular syntax. No ambiguities at all. But, that's the problem with simplicity as well because the simpler the language also the less expressive it is.
So what I found in Erlang is that I had to write a lot of repeating boilerplate and what you could call a no noise. I know some people would say that it's more explicit than that, but my personal sentiment is that it's noisier. Elixir looks maybe more approachable for the syntax, which is the least interesting part, but it has this Ruby-like syntax which many people are used to. In my view, Erlang is a more complex, more complicated language.
There are more things to learn, but because of that, you, as a programmer, can also be more expressive. It strikes a better balance for me personally. I wouldn't dare to say that either one of these two languages is better. It's more about your own personal preferences of whether you like to write a bit more and then have that boilerplate but also the simpler language, or you prefer to have more complex features in the language. But then, you also can reduce some noise. So it's a matter of personal preference for me.
There is another thing that Elixir brought to the table back when it originally appeared. I believe that José Valim started writing it in 2011, if I remember correctly. I personally saw it in like early 2013. At that time, my impression was that Elixir had much better tooling support. Basically, Erlang didn't have an official tool at the time. So you had to use some third-party tool, which now has been integrated and which is actually much better. So the story in Erlang has improved as well. But with Elixir, you just get this thing and you start this mixed new project and you do everything with mixed, and it was like very easy to build an OTP release, even back at that time when doing it with Erlang was a bit more complex, and again, required some different tools.
Even the community of Erlang didn't have this consensus about which tool you should use. There were five or more different options to do that. This is why I think, historically, Elixir worked better. I don't really follow Erlang so much anymore but my impression is that Erlang has improved as well in those areas. I like that these communities essentially move each other forward together. So we are all like the whole BEAM ecosystem.
We're saying that you can use Elixir, you can use Erlang libraries, and we wouldn't even exist if we didn't use those libraries starting from the standard library and the OTP framework but then also third-party libraries such as the popular Cowboy web server and things like that. Owing to the work on Elixir, there have been some pull requests done back to the Erlang/OTP. So, essentially, these two languages kind of work together and not just these two languages but all of the languages as they should.
So ultimately, to me, the question of saying Erlang versus Elixir is more about: do you prefer a simpler language with a bit more typing, or do you want a more expressive language but also more complex language? And again, I think that my impression is Elixir has a slightly better story in tooling and documentation, maybe support for tests, and so on.
Key takeaway no. 5
Elixir is focused on being approachable and making developers productive.
Who did you have in mind when writing the book?
Erik Schön: I think you're absolutely right there. That was a bit of a weakness in the community. I think the whole Elixir team would say they moved the community forward as a whole and now the whole BEAM community can benefit from it. So I think that's just awesome work done there. Speaking a bit about your book. I think it's really cool, and I love your t-shirt. So what kind of person did you have in mind when writing it?
What is unique about Elixir in Action?
Erik Schön: That's a very good explanation. I think that's a great starting point. And nowadays there are quite a few books on Elixir out there on the market. So what would you say is unique with Elixir in Action?
Saša Jurić: Until a few years ago, I read all the books written on Elixir. These days there are already so many that I find it hard to catch up. I'm partial to my book, but my personal impression is that all of these books are great books and that mostly they compliment each other. So, in particular, for Elixir in Action, I wrote it with a focus. Again, remember I said there were like two dimensions of Elixir, functional and concurrent. My focus was on the concurrent aspect of Elixir. Because this is where I find that any BEAM language really shines the most. This is where they bring significance to the table compared to anything else available out there.
I'm not saying that functional is somehow boring or anything, but this is the stuff that I kind of treated more as a nuisance. So that's one thing about Elixir in Action. It starts very uneventfully, the first part of the book. The first four chapters basically deal with functional programming and the type system. It's more like you have this and you have that and you have that. So you kind of have to survive through this first third but then the second part and the third part are focused on concurrency. How to think concurrently in BEAM and how to use it properly and how to use the higher level of abstraction from the BEAM standpoint or from those languages like OTP, how to use those things properly.
What I really liked about those chapters is that they're sort of modeled through my own experience. I started using Erlang in 2010. Back then, there wasn't even the famous book, Learning Some Erlang. There were a couple of books that I learned from Joe's book, for example, but it wasn't completely clear to me how I should do things, so I made many mistakes. In a sense, this is replicated in the book.
So, it doesn't just guide you in a straight line from point A to point B, but it does more of like a zigzag. I do some things wrong deliberately. You're starting one chapter and you're gonna start with something, learn some new techniques and build some implementation, and you're gonna feel good about yourself. Then at the beginning of the next chapter, I'm going to explain why this implementation is wrong and then we're going to learn something new and then we're gonna improve it. In the next chapter, you're going to learn that this is wrong too.
But I never let you fall very far. I believe that in this sense my goal was to explain not only how you should do something but also why you should do something. Because this is what I found when I was learning. In BEAM literature people will tell you that you should write like this with no explanation why. So I made some mistakes, which I've seen on forums and other exchange sites that people also tend to make when they don't know all this. This is what I personally find the book most helpful for. In essence, I wrote this book the way I would want to have it if I didn't write it in. So something that I always found missing myself.
Erik Schön: I really love that approach. It's a fantastic way of teaching because that's the way you learn in reality. It's never this straight path. It's always one step forward, half step back and two steps forward, and so on and so forth. So having that approach when writing a book is really awesome. Being a writer myself, I'm curious about what kind of feedback you got on the book and what kind of feedback was most surprising to you? Because you said that you wrote it like you would have liked it. So, what kind of feedback and surprising feedback have you received so far?
Saša Jurić: So this is the second edition. If I remember correctly, the first edition was released in 2015. So it's been over five years or so. I've got to say that I was surprised by one piece of feedback. First and foremost, I was surprised that they got any feedback. It was the first and the only book I ever wrote, and I wasn't really famous or anything before that. So there were more famous authors writing on Elixir. I really didn't expect that anyone would even read the book except for myself and my family.
But yes, it turned out like a pretty nice surprise that people actually read it, and I got some very good feedback. It feels like the book has grown sort of organically. I see people recommending it and sometimes I get unsolicited feedback. Just someone contacting me on whatever channel and saying, "Hey, I read your book. And it's a really good book. Thank you for writing it." This for me is like the best report that you can get from a book when you're not fishing for a review or anything. Or someone approaches you and just says, "I read your book, and I really enjoyed it. It really helped me." That's basically what I like.
Erik Schön: Yes. That was great feedback to get. That's the best feedback you can get: people reading it, people saying that they used it, then they applied it for real and that's what you want as a writer. So that's really, really awesome.
Key takeaway no. 6
Elixir in Action focuses on teaching not only how but also why you should do something in concurrent Elixir
About the author
Saša Juric is an Elixir mentor helping companies with the adoption of Elixir. He has many years of experience building server systems, as well as desktop applications using various languages and technologies. For the past nine years, his focus has been on building backend systems using Elixir and Erlang. He is the author of Elixir in Action, and an occasional blogger at theerlangelist.com.
Erik Schön is an executive and strategist who has successfully developed and deployed strategy for over 20 years in small, medium and large enterprises. Hacker turned software researcher turned system engineer turned manager and leader turned navigator, speaker and writer, Erik has led large, global R&D organizations in complex product development.