It’s no secret that we love Ruby on Rails at Yoomee. It allows us to be productive, iterate quickly and have fun while we are doing it. Unfortunately this comes at a cost: Ruby isn’t particularly performant when compared to other languages such as Node or Go.
Usually this is a tradeoff we are willing to accept. Most sites are fairly low traffic and don’t need to be super quick, especially in the MVP and proof of concept phases.
But what if there was an alternative that solved a lot of these problems? One that gave us the productivity and happiness of Ruby with the added benefit of speed and scalability.
Elixir is a relatively new language (2012) but runs on the time tested Erlang virtual machine, taking advantage of its excellent fault tolerance and concurrency capabilities.
Elixir has been on the radar of a few Yoomee developers for a while now, catching our attention for its expressive Ruby-like syntax, functional programming style and promise of performance comparable to much lower level languages. Coupled with the Rails inspired web framework Phoenix, we were interested to try it out and see if it would fit into our development workflow.
During investment week, three of us decided to take this as the chance to investigate the scaling problem we have sometimes encountered in Rails and see if we could build a strong case for using Elixir for high-scaling projects. It also gave us a great opportunity to introduce some Elixir experience into the company.
To test the performance claims of Elixir for ourselves we needed a comparable case with our usual Rails work, so we wrote a very simple API in both Phoenix and Rails with a single endpoint that serialized some post data. Our dataset was an anonymised large dataset from one of our current projects, making for good real-world data.
We then benchmarked these implementations to see for ourselves the kind of performance improvements we could expect.
Using wrk, we ran a benchmark of 400 http connections, for a duration of 30 seconds, the results show a roughly 6x increase in throughput.
Rails with Puma (4 workers) Phoenix
The video below show apache benchmark using 100 concurrent connections to make a total of 1000 requests, Elixir finishes in just over 2 seconds while Rails finishes in just under 12 seconds, a substantial improvement.
Testing the language
Of course, sheer performance isn’t the only factor in language choice, as any Rails developer can attest. We also needed to see if Elixir and Phoenix could provide for us in both tools and style, so we looked for a more technically interesting project to develop. One of us had been in contact with local usergroups regarding a bot battler style host capable of accepting bots in multiple languages, so we considered that as a target.
We defined the problem as the lack of an interesting, multi-language compatible competitive tool for learning different languages. Usergroups wanted something they could try different languages on and different implementations in, and a multiplayer competitive environment could provide that while also encouraging variety and creating a moving goal. The current best implementation would become the target to beat at each stage.
We ran this problem through the Lean Canvas planning process and had some difficulty justifying the implementation. We were at risk of changing the problem to justify the solution we wanted rather than understanding our problems and working to a solution from there.
In the end this process persuaded us to pivot, and we settled instead on building a simple maze puzzle server to which solver programs could be submitted. It still fitted our requirements of allowing for different experimental solvers, but had the advantage of being considerably simpler and with a clearer MVP implementation. A short maze server which had multiple simultaneous “players”, each of whose solver for the sake of the MVP would just randomly choose a direction to move in.
The game development was quite painless, and we quickly had a working build using Elixir, Phoenix and React for the front end. Most of the ‘bugs’ we encountered while coding could be tracked down to simple syntax mistakes (extra brackets, missing commas etc.) and we’ve not yet had issues with confusing app state, thanks mainly to the lack of side effects in functional programming.
The emphasis on pattern matching over over types of conditional logic in Elixir can take some getting used to. Blocks of code that would have been a case statement in Ruby become a series of function calls like this:
defp move(“up”, [x, y]), do: [x, y-1]
defp move(“down”, [x, y]), do: [x, y+1]
defp move(“left”, [x, y]), do: [x+1, y]
defp move(“right”, [x, y]), do: [x-1, y]
This code takes the player’s current direction and position and returns their next place. The code that calls the move function does not need to know which version of the function to call, the Erlang VM will pattern match based on what the first argument is.
The Rails asset pipeline is getting a little old now and can cause some real pain when developing with ES6 modules. Phoenix uses Brunch to build your JS and CSS assets, running in Node. This means you can import, use and manage ES6 modules with NPM (and any other shiny new JS tools you might want to use in your build pipeline). One warning I would give to new Phoenix users though is to pay attention to Brunch config file, all the modules you want to import in JS need to be whitelisted first in this file.
Elixir definitely demonstrates the key performance advantages we were looking for, but there’s a few reasons why we can’t just make the switch.
It’s a linguistic paradigm shift, and the associated costs in getting developers up to speed would be significant. We were interested and happy to learn the basics, but even if all our developers are overjoyed by the change it’ll still take time to get everyone up to speed.
Rails development benefits greatly from the many and varied community gems. Any common requirement you could possibly name can be acquired at a line’s change to the gemfile, a big help to any development process. In contrast, Elixir is still lacking a robust community library, so we’d have to spend a lot more time reinventing any necessary wheels.
Investment week has been a fantastic way of building our understanding of Elixir and Phoenix so that we’ll be able to identify projects which would benefit from its performance advantages properly. We’re certainly looking out for a good opportunity to use it in a full project.
Sites we found helpful along the way: