Why We Chose Elm for Humio’s Web UI

This blog was originally published April 13, 2021 on humio.com. Humio is a CrowdStrike Company.

Humio software engineers Thomas Anagrius and Jeroen Engels sat down to talk about why they got involved with Elm for web-based front-end programming. Humio was an early adopter of Elm, putting Elm code into production in 2016. Today, Humio has one of the largest Elm code bases in the world. Here’s what Thomas and Jeroen had to say about working with a very young programming language for a very modern new logging platform.  

Q: Why did you choose Elm to build a UI for Humio’s log management platform?

Thomas:I came from a place where we had been working with Clojurescript and a Clojurescript web framework called Om for a while. In Om you had a single reference to the entire state of your application and I quickly understood the power of this. I was then introduced to Elm, where this kind of architecture was built directly into the language itself which blew me away.

Elm, the language, was made specifically for writing web applications. It is a paradigm shift compared to how you would normally think about a browser application where things like runtime-exceptions are a thing of the past. Elm is a pure functional language. It’s very focused and it’s very deliberate and it’s very opinionated in how it does things. Also it didn’t hurt that Elm is a purely functional language and we were a bunch of language nerds.

Q: What were your requirements for picking a technology?

Thomas: We wanted to build a fast  interactive dashboards and fluid search experience to give users a visual representation of their log data in real-time. Dashboards sit on the walls of companies all day long, tracking systems data and applications data. Our customers want to see, for instance, how many database queries they’re running in real time. We don’t want those monitors to crash or be delayed in showing the result. It’s not okay to have a dashboard hang and think that everything is fine, when in reality your system is crashing and you don’t notice.

So in short: Speed and stability were our main requirements.

We knew we were going to be working with huge volumes of data and at the time Elm blew every other web framework out of the water in terms of performance (and still does). We wanted to represent massive amounts of log data in real time with only sub-second delay. So having as little overhead as possible was pivotal. Humio’s platform can process trillions of events a second.

We also wanted dashboards that could run for a very, very long time without crashing. Since Elm’s compiler ensures that runtime errors virtually can’t occur, we can sleep tightly at night knowing that if it compiles it runs.

Q: Why are there no runtime errors in Elm?

Jeroen: There are several mechanisms in Elm that prevent having runtime errors. First of all, Elm has a compiler which looks for potential errors, and prevents the code from compiling when it finds any. In contrast, JavaScript (JS) doesn’t have a compiler validating it before it is run, so errors are reported at runtime. TypeScript (TS) does validate the code, but it does so on the basis of a type system that is not as strict as Elm’s. It also allows you to circumvent the compiler’s checks (e.g. through the “any” type, which Elm doesn’t have).

JS (and therefore also TS) have dynamic aspects that a static type-checker can’t validate at compile time. Elm on the other hand is a very small language with no dynamic aspects, which means that the compiler and tooling has full knowledge of possible inputs and outputs at every step, and doesn’t provide any escape hatch for circumventing the type-checking.

The compiler checks for exhaustivity and ensures that you covered all. For instance, if-expressions always have an “else” expression. You need to express what must happen when the condition is true, but the language also requires you to express what happens when the condition is false.

Thomas: Or consider the case of an HTTP request. In JavaScript you can ignore handling request failures completely – e.g. if the server failed to respond you can just let your program throw an exception. Because you are not forced to deal with errors explicitly, you often forget to deal with them at all. That can lead to an inconsistent state for the application because you simply forgot that errors are always a possibility. In Elm you are forced to deal with errors explicitly or the program cannot compile.

Q: Which other languages or frameworks did you consider?

Thomas:Well, we looked at a TypeScript and some combination of React and Redux and similar tools in the beginning. We even had an implementation in React. I think the allure of working with a purely functional language and all the benefits we got from the Elm compiler quickly made us go all-in. Personally, one of things that I found very attractive with Elm was the very low number of dependencies we used. JavaScript projects are known for having thousands of dependencies even to get started on a boilerplate React project. Today, Humio has roughly 200,000 lines of Elm code, and we have maybe 25 dependencies. I think dependencies are a pretty big risk factor in software projects, and I think people often underestimate that. People just think of them as “free stuff I don’t have to write myself”. While that is true to some extent there are also a lot of downsides. When buying into a framework you also get the downsides and limitations of them – you might not feel them at first, but in the long run they can shape your application and force you to make choices that are not optimal for your product. Elm’s API and core libraries change very seldomly, so you get out of the rat race of keeping up with the newest version of your dependencies. I often think of a quote by the late Joe Armstrong:

[…] I try to write code with zero dependencies – code I wrote 25 years ago with zero dependencies still works today. Code I wrote 5 years ago with external dependencies often fails. It’s my contribution to the war on entropy. – Joe Armstrong

Q: Humio was one of the early adopters of Elm. When did you get involved? What concerns did you have about adopting a new language?

Thomas: We started working with Elm in 2016. In that first year, I kept thinking: What the [heck] were we thinking? We were worried about a bunch of problems, including how to hire Elm programmers – which turned out not to be one because learning Elm is simple. We were worried that Elm’s syntax (that most developers will find unfamiliar at first) would scare people away. We were also worried about how easy it was to debug. Since it is a compiled language you can’t see your exact code running in the browser. There were not a lot of developer tools that supported it, which was a big pain for us, and a lack of documentation. All of these turned out to be non-problems in practice.

Being an early adopter was painful at times though. When we had a problem, we were likely the first people in the world to have that exact problem. We couldn’t just look it up on StackOverflow, or find a blog that tells you how to deal with it. You can sit with a stupid little bug for weeks, trying to solve it by trial and error.

Today, the landscape is completely different. There are many examples you can use for reference, IDE support is really good and you have things like auto-suggestions for fields and type inference. The Elm community is supportive and Elm has been battle tested in production on many large projects that have had to solve similar problems to those we have encountered.

Q: Was Elm easy to learn and use? What was the learning curve like for your team?

Thomas: Like I said, Elm’s syntax is not like JS or any other C-family language. It’s not what people are used to and it can therefore be scary for people just starting out. Also you get a sense that people struggle a lot in the beginning. I have heard things like: “Why is everything so hard in Elm?” and I think one reason is that you can’t just touch the DOM willy-nilly or hack around issues like you’re used to in JavaScript. There are no escape hatches (like Jeroen put it). Hitting those roadblocks can be a source of frustration.

Elm is opinionated and restrictive. At the same time, Elm is a simple language. Once you start working with it, it makes a lot of sense very quickly. Once you understand WHY things are like they are, you accept the compiler as your friend – not your enemy.

Strong types also act as a kind of documentation for other developers. Rather than writing a lot of prose, you can just annotate it with a type and the compiler will complain if something is wrong. We’re a distributed team, so we’re not sitting next to the person we’re working with, so there’s a lot of value in having types.

Jeroen: The Elm compiler is famous for its very friendly error messages which help a lot with dealing with the problems it reports and getting used to the language.

I personally find that the learning curve is not too harsh in practice. We have newcomers go through the official Elm guide, we sit down with them to answer questions and do concrete work, and after a couple of weeks, we don’t get requests for help anymore. They may not know the idiomatic Elm solutions to all problems, but they are productive fairly quickly.

The amount of Elm knowledge that needs to fit into your head is a lot smaller than for the JavaScript language or any modern JavaScript framework, because the language is so small. When looking at any Elm codebase, you will see the same patterns and structures over and over again. Going from one Elm codebase to another will still feel quite familiar and easy to navigate through.

In JavaScript, it can be pretty hard to determine the consequences of a change. If you rename the field of an object, you need to figure out where the object is created and accessed, which can be very difficult and lead to the developer spending quite some time to do so, without knowing if they did it well. In Elm, you can simply follow the compiler’s error messages, which removes a lot of worrying and lets you move to the next problem to solve or to the next concept to learn.

Q: Are you happy with the results? Were there any “Aha!” moments, where you solved a difficult problem?

Thomas: Yes, and yes. The best thing about Elm is I am never afraid of breaking the things because of refactoring something, I have complete trust that the compiler will tell me if I did something wrong. This is a huge advantage! Elm gives you a feeling of security and confidence – for new hires this is pretty big deal. You can make big changes and do code cleanups and be confident that everything will be fine. I can’t imagine giving that up. It’s a game-changer for large code bases.

Jeroen: My “Aha!” moment was probably when I learned about data modeling. When working with Elm code, we try to prevent bad stuff from ever occurring. One of the ways we do that is by “Making Impossible States Impossible”, as we like to say (and as introduced by Richard Feldman in the talk with the same name:https://www.youtube.com/watch?v=IcgmSRJHu_8). Specifying in what states a value or a piece of the program’s state can be, and therefore preventing all the other possibilities, is an amazing tool and guarantee to have. With that in mind, you start to think about all the tools and techniques at your disposal to add new guarantees about the code across the project, so that you can stay focused on the happy paths.

Q: Is there anything you would have done differently, if you could turn back the clock, now that you have more than 4 years of insights in a production environment?

Thomas: Yes. One thing we could have done better was to make reusable components earlier in the development process. We were a startup and very focused on moving fast and making customers happy asap. I guess not making reusable enough components is not an Elm-thing, but the fact that Elm was a young technology played into it – I think. It wasn’t clear exactly how to do good reusable components in Elm – we didn’t really have any good references to get ideas from. That has since changed, and there is much more community material and examples available.

Today, we are very happy with the choice, we believe in the paradigm and it is a great fit for Humio. I would recommend Elm to anyone considering it. There are definitely trade offs compared to JavaScript, just like we have already mentioned. But from a long-term perspective, the benefits of an Elm-codebase compared to JS greatly outweighs the downsides.

Related Content