Why we choose Elixir as the main technology for our startup?
Elixir is one of the young functional programming languages people mentioned a lot. We have been working on Elixir and Phoenix for several months since we started the business and we would like to share why we chose Elixir, the lessons learned, sample code and why it might be a good choice for your next project.
This article is the summary of my speaking session in Elixir Casually, the very first Elixir meetup in Thailand.
One of the Elixir’s advantages people talked about the most is the “Concurrency”. As Discord also mentioned how they handle 5,000,000 concurrent users with Elixir. Let’s first take a look at the concurrency models of the other languages to understand Elixir.
The scenario is when there are 2 users (customers) are accessing (ordering food) our web application (kitchen). How would each language handle the requests in their common ways?
Concurrency in NodeJS
NodeJS uses asynchronous request handling. which means it won’t let the customers wait in the queue to order. The kitchen accepts all incoming requests and tells them to sit and wait (returning Promise). And the kitchen does several things at the same time and send back the food (response) whenever any request is done first. This is pretty good for utilizing existing resources to the fullest. However, it cannot utilize all CPU cores well unless we use process spawning.
Concurrency in Ruby on Rails
The concurrency in Ruby on Rails is quite simple. Who comes first get to send the request first. And the next customer needs to wait until the first customer receives the food. This way helps make programming simple but it doesn’t utilize resources well. For example, while the application waits for IO, it does nothing else.
Practically, we use a worker or spin up several kitchens or instances of Rails to accept the requests. The downside is that a worker is not very simple to manage and 1 instance of Rails can eat the memory up to 500 MB! We have to pay a lot for a cloud service to just host 20 instances which require 10 GB of memory.
Concurrency in Elixir
In Elixir, for every new order, the new kitchen can be spawned to handle the request. So it is as simple as Ruby yet powerful. We can possibly write business logic without asynchronous parts. And it will utilize all existing CPU cores to the fullest. Also, when one of the instances crashes, it won’t affect other instances and requests.
Moreover, each instance, which we call it “Process” (Erlang process, not the same with OS process), will be taken care of by a supervisor. The supervisor will monitor and restart each child process. That’s why it has great fault tolerance. We can run a bare Elixir app for years without going down.
And one of the best things is that 1 Erlang process frequently takes less than 1 MB of memory. And it can be as small as a few KB for a very simple process. So we can easily run 10,000–100,000 processes in our own laptop.
This Elixir’s concurrency power is from Erlang’s OTP (Open Telecom Platform, not One-Time Password 😂) which is built by the engineers from Ericsson. Their goals were to build the platform to handle millions of the phone call connections and routing in the distributed system with the lowest latency and without going down even when the new version is deployed.
To think about it, the telecommunication system is one of the most sophisticated systems that can handle millions of connections yet almost never goes down.
So Elixir or Erlang is the great choice for real-time or WebSocket applications with a huge number of concurrent users like chat app.
So We chose Elixir because of the concurrency?
The answer is… not really. We were excited about the concurrency but that’s not the biggest point.
We do have the use cases which our system needs a lot of processes at the backend to monitor and interact with several blockchain platforms which really help us well.
But as a startup, productivity and speed of iteration are super important. I personally like Ruby on Rails for the aspect of productivity. When using Rails, we spent most of the time on solving business problems rather than solving framework problems or choosing the tools.
Ruby has an expressive syntax, reading a written code feels like reading a book. And their community is quite unified and opinionated, they have a strong convention over configuration which helps us think less when building something.
However, scaling in Rails is not very easy. So when we found Elixir, it has the combination of developer experiences of Rails and the power of 33-year-old Erlang which I believe Elixir will be one of the most preferred languages in the future.
Fortunately, as I ever worked in Omise Payment, our CTO and many engineering colleagues like Elixir and I have seen several projects in Elixir which really impressed me how much clarity can the code base have. And these projects really prove that working in Elixir is practical for general problems. These experiences made me confident to choose Elixir for our own startup.
A programming language is supposed to be for human communication rather than just a tool for talking with a machine.
More importantly, whenever we learned a new language, a new framework, it creates a paradigm shift for us. Just like when I learned Ruby on Rails after using NodeJS, it taught me the clean and solid way of organizing OOP application. As a result, I ended up writing better NodeJS apps. So Elixir is another paradigm for us to learn to solve problems better.
Pros and Cons in Elixir
From what mentioned earlier, here are some more points on the pros and cons in my point of view.
On the pros, I will explain more in the later part.
For the cons, on Erlang learning, we almost don’t really need to know Erlang but it is better to know some, especially OTP part. And some Erlang wrapper library may throw Erlang error stack trace which can be confusing.
About learning resources, on starting, we lack the best practice and the sample for the real Phoenix projects. It took us quite some time to get to a good code organization. Still, thanks to OmiseGO for building a nice open source Elixir project we can learn from.
On the community, it is still small yet very friendly and active. We can ask questions about Elixir in the forum and we will frequently see the answers from José Valim, the creator of Elixir. I opened the very small PR to the core Elixir library and he came to merge in 15 minutes 🤣.
Show me the code!
Okay, I’m going to walk you through the different problems to see how Elixir helps us solve the problems with clarity.
Elixir — Everything is immutable.
Elixir — since it’s immutable, doing like above won’t work.
Let’s refactor to make it better.
Let’s use Pipe Operator
|>. And it becomes direct and simple like the documented procedure ❤️
In Elixir, Pattern matching is very powerful. When we see equal sign
=, it is not an assignment operator but the matching operator.
Once we installed Elixir, We can type
iex in the console to play.
We can see that it will return the value if it can match. And when there is a variable, it will do a matching following by assigning the value on the left side only if the matching passes.
A New Way of Solving Problems
Let’s see more practical usage. Assuming you have to develop the Tinder matching function
match(my_feeling, target_feeling). Let’s pause and think a little moment, how would you implement this in your preferred language?
Let’s take a look at the quick solution in Elixir.
You might notice that the written solution is almost the same structure as the requirement table above which is very easy to implement and extend. The underscore
_ means it will match anything.
It is possible to use a pattern matching at params level to write like this too.
Let ‘s develop the Fibonacci program. In an imperative way (non-functional), we could do for loop to solve it. But how is it like in Elixir?
Without thinking much, we can try listing output from the input
And once we notice, we will see that the top 2 lines are base cases that we cannot compute while the rest can be calculated from the addition. Let’s refactor!
Alright, it’s done! We have done the implementation easily and it is quite clean. I’m satisfied with this solution. But someone might say that we can actually reduce redundancy for the base cases. So let’s try.
We can use
when n < 2 to control the scope of the params going into the method. We called this constraint
Note that this implementation is simplified and not optimized which can have a performance problem. You may check the sample code here for how we can optimize.
Testing in Elixir
Elixir has built-in ExUnit module for testing which also supports async tests. So we can do this without adding any 3rd-party library.
We can see how much developer experience has been put into building Elixir from the test error result. Normally, we would have the convention to put Expected output and Actual output on left or right which can be confusing.
It displays the line of code which goes wrong. And it tells the value in a simple way,
right which I like this.
Elixir has first-class documentation support. For example, it has syntax to describe the module and method with
The problem we sometimes find is the document lies to us! 😡. It could be a typo or stale comments. But the document examples cannot lie to us in Elixir!
Let’s say if we write the wrong example below.
And when we run tests, it will yell at us!
Note that we also need to put
doctest Fibonacci in the test.
With this way, we could move most of the simple tests from the test files to the implementation files which helps a lot since we usually look at the test first to understand the logic.
Even better, we can easily add
hex library to automatically generate the HTML docs with the command
mix docs which gives us the nice documentation from the source code documentation!
And all Elixir libraries use the
hex tool for generating the docs. So we will see the same and nice format of documentation everywhere. 🥰
Unified and Rich Ecosystem
The last thing I want to mention is about the rich ecosystem. Most of Elixir developers use the same tools which help us think less about choosing tools. And most of the things we needed are built-in to Elixir or at least built by the Elixir core team.
In the simple or early application, we almost don’t need to use something out of the ecosystem. It helps us get started faster, delay or even get rid of the need to invest in more technology like Kafka or Redis.
Try it out!
Hopefully, this article helps you get more ideas of Elixir in a short time. But you won’t really understand unless you try it yourself. Head to Elixir School to see the installation instruction and you can open your console to learn it in a brief moment. You might find Elixir the right tool for your next projects. 😊
Let’s learn to solve the real world problems better! ✨
- You can find most of the code samples I mentioned in my GitHub repo.
- For another hands-on Elixir Load Test session in Elixir Casually by Unnawut L., you can check the code and result in his Github Repo.
- Thanks a lot to the staffs who organized the awesome event Theesit, Pallop, Peerut, Pongsatorn, Ranatchai, I'Boss, Unnawut and W.S.Kiang
Our team at Flipay is creating the Borderless world of Banking by building the gateway for people and business to access the power of Cryptocurrency and Digital asset seamlessly.
We are now hiring Software Engineers to join our international team in Bangkok! If you are interested to build the awesome products with Elixir,🔫 shoot me a message or email me at firstname.lastname@example.org anytime :)