Some time ago, I was discussing the possibilities of Kotlin and Elixir with a colleague. Our rather academic exchange only lent further credence to the notion that every developer has their own point of view and specific needs. If you’re working with Kotlin, it might be hard for you to understand the hype around Elixir, and vice versa. And this, in turn, might lead one to outright ignore a particular technology.
To my interlocutor, Elixir looked like a well-designed technology, but lacking convincing innovations. He said:
Everything that Elixir has under the tag line I have already solved in my Ruby setup. Why should I spend precious time learning a new solution if I already have one?
That's a fair comment and proof of a very common-sense approach to new technology. Brought to a logical conclusion, however, the approach might also lead you to question how exactly does Erlang VM's ease of distribution benefit you when 99.9% of apps you develop will work fine with a single, mediocre server.
We decided to set up a simple challenge—creating a presentation for a local Ruby meetup that would explain our preference for a particular technology and identify those individual elements that constitute much of the pull of those languages.
So, without further delay, let me show you why I think Elixir is a great framework.
Why use Elixir Phoenix
First off, let's define the type of issues I am trying to solve by introducing a completely new tech stack. Naturally, the list below is highly subjective and not all of the points might apply to you or your case, so YMMV.
Let's see what are the pros of Elixir.
1. Surprises
Ruby's flexibility is very powerful. It's hard to find a better language for creating DSLs. Ruby code is usually concise and easily readable—it's easy to see why it’s one of the greatest scripting languages out there and a joy to work with. And, hey, developer happiness was one of the most important factors that drove Ruby’s design. Alas, then Mr. Hyde showed up. In Ruby, there is often too much magic that ends up being confusing, error-prone, and hard to understand and debug. Also, due to its very nature, it’s not possible to fully tap into the power of static code analysis and draw on the refactoring support offered by tools that other ecosystems happily utilize.
Raise your hand if you ever struggled withNoMethodError (undefined method for nil)
or with overwritten default behaviors of the standard library.
One of the benefits of Elixir disallows the overriding or reopening of any modules (and, frankly, I can’t recall the last time I needed or wanted such feature)—it loads everything from thelib
and allows import/alias for a shortcut. The capture operator is one of the magical things in Elixir.
Elixir offers a great mixture of conciseness and beauty, augmented by a general lack of surprises. Thanks to macros, it provides an even more powerful foundation to create DSLs on (although they’re probably a little bit harder to introduce). Just take a look at what Ecto (a popular library for the DB layer) can do:
from p in Post,
left_join: c in Comment,
select: %{p | comments_count: count(c.id)},
group_by: p.id
order: [desc: p.published_at]
The code above is Elixir spiked with a few Ecto macros! You can also chain such queries and reuse them in different parts of the app. Because they’re compiled and not just interpreted in the runtime like in Ruby, macros are also more performant than Ruby DSLs.
And, last but not least—under the hood, Elixir compiles to BEAM, which makes it trivial for static code analysis.
2. Functional Programming
I'm more of an FP person. I graduated from the University of Wrocław, where students learn about functional programming during their very first programming class.
You can joke that FP has its own "This finally is the year of Linux on the desktop" slogan, but the truth is that even hardcore OO languages like Java are beginning to introduce some FP traits and it's no longer a hipster thing to program in a functional language.
Ruby itself has a quite strong FP flavor, but, on the other hand, still prefers side effects, e.g.:
(1..1_000).reduce({}) do |acc, x|
acc.merge(x => 1)
end
(1..1_000).reduce({}) do |acc, x|
acc[x] = 1
acc
end
The second version is around forty times faster on my machine!
Functional programming means small functions, immutability and no side effects—and I have strong inclinations to force such traits in my Ruby code, even when they don’t fit.
If you're a member of the "Functional Programming is Scary" club, then you should know that Elixir works really hard to make the transition from OOP as smooth and painless as possible. Like Ruby, Elixir focuses on developer experience and removes unnecessary complexity wherever possible.
3. Distinctions
The slogan of the Phoenix framework is “Phoenix just feels right”—and God help me if that's not a true statement. While you can still write a blog engine in 15 minutes, the framework makes a clear distinction between, well, the framework and your business logic. Phoenix encourages thin controllers that simply utilize YOUR API wrapped in different business contexts with 100% of your code. Almost everything is explicit and easy to customize. It provides sensible defaults and fits both rapid development as well as something designed to live for ages.
And if you ever struggled to adapt Active Record to the repository pattern, you will find yourself happy to work with Ecto.
4. The Future of the Code
José Valim, the author of Elixir, claims that the Elixir language is in a stable phase and there are no plans for any revolutionary changes ahead. Instead, the core team wants to focus on improving the ecosystem. They exhibit a common-sense approach and add new features to the language only if they benefit most of the community. If they won’t, the core team will instead encourage implementing such features as a third-party library.
Benefits of using Elixir
Elixir is far from being a revolution. I don't need one. The only revolutionary thing here is making the FP paradigm more popular and approachable. Apart from that, it offers a lot of small evolutionary improvements that you might not notice at first glance, but which might compensate for the lack of all the gems that you won't have here (or would never find because no one would need them).
1. Pattern matching
I already mentioned pattern matching, but it's really hard to emphasize the power of this feature enough. Different function signatures allow you to make much clearer intentions of how the code should behave in different cases. It helps make things concise and clean. It can even help in very ordinary places, e.g. imagine Ruby pseudo-code like below:
def foo(arg)
val = bar.call(baz, arg)
...
end
Have you ever been hit by a bug in code like this because you had forgotten about edge cases that generate results different than expected? How would you protect yourself against it? Maybe like this?
def foo(arg)
val = bar.call(baz, arg)
raise "not expected return" if val.nil?
...
end
Elixir offers a different approach—pattern matching:
def foo(arg) do
{:ok, val} = bar.call(baz, arg)
...
end
which will fail ifcall
returns something else than a tuple with the first arg being an:ok
atom. Otherwise, it will assign the second element of the tuple toval
. As a side note—such idioms force you to think about handling different cases that might happen.
It's just the tip of the iceberg but it should already impress you how powerful and useful this feature is.
Do you like Structs? You will appreciate them in Elixir thanks to pattern matching:
%MyApp.Foo{bar: bar} = baz()
2. Elixir's Performance
Let's make it clear — Elixir is not fast. If you benchmarked similar solutions written in Ruby and Elixir, chances are that you’d either get similar results or Ruby would actually prove faster. But here’s the thing—similar solutions. Solutions in Elixir will be different. Probably simpler. Easier to scale, for sure, even if scaling will only mean using every CPU core available on the machine. And that makes programs written in Elixir fast. Sometimes fast and furious.
3. Docs & Tests
Almost a decade ago, I decided to go with Ruby instead of Python, but since then I have been missing the care the Python community demonstrated when providing documentation. Elixir is the same, but goes even further— it's not just nice to write docs, it's good for you. Why?
Two reasons, at the very least—you can run them as a test, which is often enough for simple functions (no more excuses like "I don’t have to write a test for something as simple as this!"). And you can quickly access them from the terminal with h IEx helper.
Additionally, if you annotate your functions with types, you’ll receive really powerful support from Dialyzer.
4. Domain-Driven Design
The Web is stateless, which means that every request has to recreate the state. And that can be a huge waste.
As we know, caching is hard, but sometimes there’s not that much you can do about it. DDD, especially when implemented with a full spectrum of patterns and approaches, like Event-Driven architecture and Event Sourcing, has a lot of places where statefulness could improve performance a lot—for example, you have to introduce isolation on every action on Aggregate, where you recreate it from the history of events and apply a new event.
Elixir utilizes the Actor model (well, that’s not 100% true, but that's a different story) as lightweight processes. What does it mean? It means that every aggregate can be loaded into a process and just live there as long as you wish (or until the app restarts), and the process itself will guarantee the required isolation. You will need to recreate it from the database just once—after booting the app up.
It's pure pleasure to work with a language that supports your way of thinking with its killer feature abilities. And gives you performance gains in the very same places where other environments hamper you with performance hits or require an infrastructural overhaul.
5. Interfaces
Elixir is not OOP, but provides something very similar to interfaces—behaviors (and protocols, which are better than inheritance). Sometimes, it's convenient to make it possible to define a contract in code and get some support that verifies such contract.
Fundamentals of solid system design
It's probably not too important, in the grand scheme of things, whether you are using a language like Erlang or not. While I do feel it's under-used and under-rated, the biggest benefit of it is not running a system that uses it. The biggest benefit comes from learning about the fundamentals of solid system design and internalizing its lessons in a practical context.
I like this paragraph from the “Ten Years of Erlang“ article. It offers a really good explanation of why you should invest your time in this ecosystem, even if just for a short while, even without the intention to use it in production. It can be both joyful and gratifying.
There’s a high chance you will take some lessons home to your playground. Or stay for good.
What is Elixir used for?
1. Bet365
Bet365 is an online betting and gambling website, with more than 255 million monthly visits.
During peak times, Bet365 handles even 100,000 concurrent users. Bet365’s former backend stack based on Java was increasingly having trouble handling such a load. Also, Java’s verbosity proved to be too complex to manage and scale, causing cognitive load.
The company made a move to switch to Erlang first and then enhanced the stack with Go and Elixir. The combination of these three languages resulted in a highly scalable platform that is much easier to manage and maintain.
2. Financial Times
With its roots dating back to 1888, Financial Times has over 1 million paying readers (as of April 2019) and over 23 million monthly visits.
Monthly visits for Financial Times. Source: SimilarWe
Delivering undisrupted services to such a high number of users required abandoning the microservice-based REST APIs architecture and turning to a much faster GraphQL API solution built in Elixir.
By the way, Monterail was featured in the Financial Times 1000 Europe’s fastest growing companies in 2018 & 2019.
3. Meltwater
Meltwater is a SaaS (software as a service) platform for monitoring social media engagement. Meltwater is equipped with advanced analytics algorithms to help it comb through millions of posts, news, and blogs, tracking down content pertinent from the perspective of a user’s business.
To maintain its effectiveness, Meltwater relies on a combo of technologies, including Elixir.
4. RD Station
RD Station is a digital marketing automation and management tool, designed to help companies get more leads, improve growth, and increase sales. The platform is a very robust tool, full of marketing features, including actionable customer journey analysis, in-depth campaign analysis, and a code-less landing page editor.
As in the case of Meltwater and Bet365, such a collection of features requires a mix of reliable and stable programming languages. RD Station’s backend relies on Python, Node, and Elixir, among others.
5. Inverse
Inverse is a news website, covering topics ranging from technology through science, entertainment, and culture. In 2015, Inverse started incorporating Elixir into their tech stack, ultimately letting it spread all over the backend architecture in 2016. Since Elixir was introduced, the website has been able to swiftly handle any spikes in traffic.
Spike in traffic for Inverse. Source: Quora
Michael Schaefermeyer, the co-founder of Inverse, said that only two servers were needed to handle that big spike in traffic; their performance also remained intact.
According to SimilarWeb, Inverse currently serves roughly 12 million monthly visitors:
Total visits for Inverse.com. Source: SimilarWeb
If you feel Elixir has potential, just give it a try. I am sure you will not be disappointed.