Re-writing the site of Norway's largest transport provider in Elm
Written by Robin Heggelund Hansen and Kjetil Valle
Vy is the largest transport provider in Norway. Their website has over a million visitors each month, and people depend on it every day to purchase bus and train tickets. Another interesting fact about this website is that it’s almost entirely written in Elm.
We have had Elm in production at Vy since late 2016. We started out by testing the language out on a small internal application to get a feel for its capabilities and limitations. We found that the strong type system prevented several classes of bugs in our application, and the alluring promise of no runtime exceptions was confirmed with a big boost to our productivity. In short: we really liked it! Yet, we were still were unsure whether it would scale to “real use”, both in terms of making something more complex for end users, but also in terms of introducing a new language to the team — and a functional one at that.
To test out the latter, in the summer of 2017 we tasked a team of summer interns with the renewal of our seat map application, a crucial component of the ticket booking process. They were use Elm, a language they had no prior experience with. To our surprise they took to the language very easily, and their work turned out great. The application they wrote is still running in production with only minor changes since.
This experiment gave us the confidence we needed to try out Elm on a larger scale. As luck would have it, a decision had been made to re-write our entire booking process. Vy’s vision is to make it easier and more accessible to purchase tickets, so that even more people can travel using environmentally friendly public transportation.
Fast forward to today, and the entire booking process has been rewritten in Elm, as well as most other parts of the website.
New developers quickly became productive
We have found it surprisingly easy to get new people up to speed with our projects. Starting out as only a few die-hard Elm fans, we have quickly grown to 15+ Elm developers spread across multiple teams. Several rounds of summer interns have also proven that it is possible to learn Elm and our systems, and become productive in a matter of days. We have achieved this through a combination of “Intro to Elm” workshops and liberal use of pair programming.
An approach we have found especially useful is to find out what the person is most comfortable with, and start with those parts of the code base. For instance, for someone with deep knowledge about HTML and CSS, we will probably start by having them work on the view code. For someone who primarily knows backend coding, perhaps working with the API integrations is familiar ground, and a good starting point. Once they feel comfortable, it’s easy to expand and have them look at new aspects of the application.
There is boilerplate, but that’s fine
We believe that a big reason behind our success in getting new developers rapidly up to speed is due to some of the core design choices behind the language. Namely that Elm is a small language with features that favor explicitness. The type system is involved in everything you do, and there are few tools to abstract away repetitive code outside of wrapping things in functions.
This has lead to Elm being criticized for not having the tools to avoid boilerplate. In our experience, however, this is a good thing. Because of how explicit Elm apps tend to be, it’s easy to understand what the code is doing without being very familiar with it. To figure out if a function can perform any kind of side effect, we can simply glance at its type signature. Elm apps also tend to be structured in the same way. If we want to see what sort of things can happen, we simply look at the top level update function. If the app is structured a little differently, it’s easy to figure out how things are meant to work by simply starting at the top level Main.elm file.
While boilerplate tends to be viewed as negative in many languages and frameworks, we have found it to be a simple and important guide to how a frontend application is set up, and by reading it we’re able to better understand how a given system works.
It does have rough edges
We’ve also had our share of frustrations. As mentioned earlier, some of our code resides in packages which are re-used in most of our frontend facing apps. Elm, however, doesn’t support private packages. We ended up using a third party tool for this, called elm-git-install, but it doesn’t solve all our issues. It works fine when we have an Elm application relying on a private package, but private packages are not able to rely on other private packages. This has forced us to include a frontend app within each package repo for the sole purpose of building the package with its dependencies, so that we at least know it should work outside the repo.
While this is not a show stopper, it is a little awkward and there are several developers who view this as a sign of language immaturity.
Another thing that can be an issue is the small number of third-party packages. This is partly due to Elm’s young age, but also because of a core design decision in the language.
Ports work asynchronously, which is fine for certain things like local storage or websockets. But for validating phone numbers, which ideally is a synchronous operation, this introduced a lot of unnecessary complexity. We now had to consider when phone number validation occurred, and what could happen if the user was to click the submit button just after entering their phone number. This did end up causing a few bugs which were surprisingly difficult to find, given the size of the feature.
Elm is here to stay
The thing that got us interested in Elm in the first place was a wish for a good static type system, along with the guarantee of no runtime exceptions. After two years of experience with Elm in production, we’ve found that these promises result in a definite boost in our productivity. Team members, both experienced and inexperienced alike, feel more confident when making larger changes to the code base than we do in other languages.
Because of this, it has been easy to scale from a few Elm enthusiasts in one small application, to having a significant portion of our website in Elm, supported by a growing team of Elm developers. When refactoring is easy, a lot of the pain associated with scaling fades away.
A common misconception is that it is risky to use a non-mainstream language, since it will then be difficult to find developers with the right experience. We have found, however, that we don’t need people to know Elm beforehand. We have onboarded people with different technical backgrounds, and they tend to pick up the language rapidly and become productive within days.
In total, our experience working with Elm has been overwhelmingly positive, and we look forward to continue working with it going forward.