Elm: A frontend story that a backend dev can love

I’ve been doing Web development for about 15 years now. The first few years were mostly tinkering with PHP and doing basic changes via the browser using Plone

Very early into my career I co-founded Niteo. And got the first few clients. We were n00bs, so we decided to use Plone to build client websites — it had a nice default UI for those years. I mostly worked on business logic customizations and add-ons. The UI part usually consisted of using correct classes and relying on Plone to make sure the frontend looked OK-ish. We weren’t winning any design competitions, but that was not our goal. We were going for pragmatic, usable interfaces.

This mantra stayed with us for many years. We started using Twitter Bootstrap soon after it was released. We were freed from the constraints of the Plone styling, now we could build UIs that looked kinda modern! Sweet! But we still kept very close to default Bootstrap look. It was “good enough” and I really wanted to avoid writing JavaScript.

I tried to learn to like JavaScript several times. But compared to Python it always seemed such a stress-inducing experience. There’s no great way to debug my scripts, let alone have a good testing story. I like to stay in my terminal where I can use the keyboard to move around, not my mouse. Clicking around in the browser to develop & debug code feels like using my left hand for writing. It’s not impossible, but I’m certainly not enjoying it. Moreover, in Python-land, we had 100% test coverage of our packages already back in 2013. The JavaScript testing story in that year was rudimentary, at best. 

2013 was also the year when Niteo transitioned from a consulting agency to a product company. We had ideas for SaaS products, and we went all-in: I “fired” all our customers to be able to fully focus on our first project: easyblognetworks.com

We had to choose the technology stack. I evaluated latest & greatest JavaScript frameworks (backbone.js was hot those days) and felt sad how bad the support for unit-testing was. We quickly decided that we’re gonna stick with Bootstrap for the next couple of years, and render all our HTML on the server. Yes, the drawback was that our SaaS will not have a very dynamic interface, but the upside was much bigger: we can properly test HTML output and make sure we don’t break things when pushing out new features. We’re a small team, and the only way to get anything done is to not have bugs in production.

In 2015, I had a few weeks of not being super busy and I took the time to re-evaluate our stance on building UIs. Vue.js was hot at that time, so we bought a couple of books. Testing was only briefly mentioned in them. Argh! Back to server-side rendering, safely in Python-land. 

Last summer it was time for us to start working on a new long-term project: WooCart. With a project that we planned to launch in 2019, we really didn’t want to be stuck in the 00’s way of doing Web UIs: server-side rendering of HTML pages. We had some budget from our past successes to overcome the initial learning curve of Doing-It-Propertly™: backend is serving an API only, and all HTML is constructed on the frontend. We went with React because it was the coolest kid on the block. Or that’s what people told us. We hired expensive consultants with years of React experience to help us build up the framework, teach us how to develop in React-way, configure testing and CI, etc. This time, I’m *really* learning JavaScript and I will love it!

After just a few months with React that old feeling was creeping over me: I hate this. Unit tests are auto-generated from code meaning you don’t really think deeply about them, they are very long and hard to read. Moreover, they are slow to run, and they are fragile. It’s hard to introduce changes. Things break randomly all the time. We have this fantastic development experience in Python-land, where we can automatically deploy from master several times a day, relying on our test suite to catch potential regressions. I really like working in an environment where the computer is my safety-net and helps me out. Why can’t we have such a safe environment for our frontend? After 10 years of searching for a decent frontend story, React is nowhere near close to what I would need to be comfortable and to enjoy it!

In my desperation, I decided to go completely in the other direction. Forget about the coolest technology of the day. I remembered that when I started with Python, people laughed at me. It’s not a real programming language, they said. No-one uses it and no-one will pay you to write Python. But I recognized Python’s main advantage: readability and a good testing story. These two combined allowed me to adapt to change more quickly. And change is the only constant in today’s world. Especially in a young company trying to build products that people will gladly pay for. 

Many good friends from Python have recently moved to functional languages. And they are vocal about them, much like we were vocal about Python a decade ago. Many of them raved about this new approach to building frontends: Elm. Since functional languages are so far off what I am used to, and having tried and completely failed learning Haskell some time ago, I knew I cannot just jump into Elm and expect I’ll be productive. Instead, I started watching talks and listening to podcasts. I found myself eagerly awaiting that next long drive so that I’ll have a few hours to myself, listening to people explain the design decisions behind Elm. 

Months of listening to talks and podcasts passed by, and I was more and more convinced that Elm was the way to go. That’s *before I knew how the syntax looks*. I just didn’t care at this point. I wanted something that was designed from the ground up to make my life as a developer as easy as possible. Elm’s promise of no runtime errors and helpful compiler messages that guide you to a solution are exactly that. Finally, at the beginning of this year, I bit the bullet and went through my first Elm tutorial. The syntax was strange, but I got used to it.

It was time to build the first mini project in Elm. People have said that JSON encoding/decoding is difficult in Elm, so I looked for a project where there won’t be any AJAX requests to REST APIs. Something that lives on it own. And I found it: a Salary Calculator for (future) Niteans. Two afternoons later I had the first version up on https://niteo.co/salary-calculator. I have never ever felt so productive writing frontend code. I enjoyed how I didn’t fear change. I felt comfortable throwing things around knowing the compiler will catch me if I fall. 

Two more things that I have to mention that greatly helped me in those first weeks:

  1. The #beginners channel on Elm’s Slack — one of the nicest and most helpful folks I’ve met in years.
  2. Hiring an Elm mentor and stating clearly that the job is to teach me how to write Elm code, not to write it for me. Whenever I got stuck, instead of hours of frustration, Tad helped me cross the gap in minutes.

We’re now 6 months into using Elm in Niteo. Besides the Salary Calculator we use it in two more projects. And we couldn’t be happier! This is such a stark difference compared to all my attempts at using a JavaScript framework in the past decade. Not only that I don’t hate Elm, I love Elm! I can’t wait to be able to schedule a chunk of my week to hack on one of our Elm projects. 

Having the compiler inspect a purely functional, strongly typed codebase feels a lot like that day in 2013 when we reached 100% test coverage on our Python codebase. From that day on, any refactor was easy. I no longer feared doing cleanups because tests would catch potential regressions. Knowing that the compiler will check my code feels exactly like that. It’s like having a personal butler that writes unit tests for me, as I write features. Obviously, we do still write tests for our Elm code, but they tend to be a bit more higher-level tests, checking that things make sense from a 30.000-feet view. However, the majority of tests, unit-tests, that make sure code behaves well in edge-cases, are redundant in Elm — the compiler makes sure you don’t have loose ends in your code. 

Finally, in June I attended the elm Europe 2019. It was a small conference, held in a college lecture hall in the suburbs of Paris, far from the “must-go” conferences of the day with shiny booths, thousands of people and aggressive hiring from mega corporations. Come to think of it, the conference was exactly like Python and Plone conferences back in the day: zero bullshit, very welcoming folks and fascinating talks. It was my final confirmation that Elm is the way to go.