At Very, we’ve standardized on React as our primary front end development framework. There are plenty of reasons why, but the highlights are:
- the large community
- the broad range of platforms to which we can apply the same code
- the general ubiquity of JS in the developer skillset.
Recently, however, a contingent of developers has taken an interest in Elm — and we’ve run into a challenge: how can we can continue to deliver consistent, timely results while simultaneously satisfying developers’ desire to develop new skills and experiment?
Incorporating a new tool into a company’s toolset can be expensive, which is why we follow a strict adoption policy for new tools, with the key step here being Validation:
Validation is one of the core facets of disciplined change adoption. To start validation, you need a clear hypothesis, experimentation description, and quantitative measures of success to measure the experiment against. It's helpful to pick a small, isolated environment to conduct the experiment - either a super low risk client project where the change would not impact timelines or quality in a non-trivial manner; or an internal project that can sustain increased defects or inefficiencies as the result of learning a more complex tool or process.
I won’t attempt to execute the entire adoption change policy in this post, but I would like to supply a hypothesis — and a few arguments — for why Elm would succeed in such an experiment.
As stated above, we want to achieve two primary goals. First we want to improve developer happiness by allowing devs to work with interesting, enjoyable technology. Second, we have to continue to meet our business objectives without losing much of our current productivity. Therefore, our hypothesis can be stated as such: Elm will help improve developer happiness while continuing to enable high quality, timely deliverables.
Elm: the good parts
How does Elm do it?
The first and perhaps most obvious example is Elm’s compiler. Its goal is to be the coder’s constant companion during development. Error messages are designed to be as helpful as possible, providing suggestions to help resolve errors:
When used with type annotations, the compiler becomes even more powerful. Elm will infer types without the annotations, but sometimes it’s hard to pinpoint where an error originates without knowing what types to expect. In these cases, Elm will follow the code as far as it can until the type ambiguity leaves Elm without a guess as to the correct type. With type annotations, however, Elm’s compiler can directly identify errors without any questions.
The compiler will do more than just report errors. It lends itself to a style of programming that can be described as compiler-driven development. Many popular text editors have plugins for Elm and will report compiler errors directly to programmers as they type a program. These error messages can help the developer decide which action to take next.
Elm’s strict type system and optional type annotations facilitate the development of code that is very maintainable and accessible to new developers. Old code can be removed without fear of side effects, and new code can be added or refactored securely with the knowledge that the type system will protect the code.
The type system’s effects go far beyond code. Elm has a built-in package system with enforced semantic versioning, made possible by its type system. elm-package is the tool that developers use to create and publish Elm packages. It enforces the rules of semantic versioning and automatically increases versions after code changes with the `elm-package bump` command.
elm-package is included with any Elm install. One of Elm’s core tenets is that it’s “batteries included,” which means many common development tools are installed by default when the language is installed — helping developers to get on their feet fast. These include:
elm-make - the build tool
elm-reactor - an interactive development tool
elm-repl - a repl environment
elm-package - package management
In addition to baked-in tooling, Elm — by combination of its nature as a functional language and intentional design decisions — has several other built-in features that we’d have to use plugins for in other JS projects. These include the aforementioned static types; data immutability, which also helps boost language performance; state management, which would require a big package like redux to get the benefits of in React land; and a strict style guide that can be enforced with packages like elm-format, which leads to applications with consistent, familiar appearances. This is in contrast to the JS community’s softer approach to style guides and tools, like eslint, that allow for massive customization.
Even with these features, Elm wouldn’t be useful in many front-end systems if it couldn’t work alongside JS (and the myriad JS view libraries). Fortunately, it does. Incrementally adding Elm to existing projects is a great way to evaluate Elm as a tool, learn it, and compare it to whatever tooling is currently in place. Elm also supports connecting to vanilla JS via ports and flags. With ports, a user can call out to external JS libraries and receive update commands that inform the Elm program how to behave. Flags allow the user to pass certain variables into an Elm program on start up, such as the current window.location or a Date object.
Elm: the rest
There are a few downsides to using a tool like Elm. It has a small community, especially compared to the JS community or even the React community. Because it’s backed by Facebook, React has a built-in user base that drives development and community. While quantity isn’t always the best indicator of quality, in programming communities, it tends to provide oversight and ensure quality in the community-championed packages.
Of course, Elm isn’t fundamentally failing because of these limitations. Every language that exists must be designed to fit the priorities or goals of the developer, and therefore will have to make tradeoffs that negatively affect some percentage of users. Because it’s designed to maximize developer happiness, Elm is willing to sacrifice some flexibility.
It’s unlikely that Elm will become Very’s default language for front end work anytime soon. That’s due in part to its narrow, web-based focus (we’ve often used React as a part of react-native for mobile projects); in part to exciting developments in front end frameworks, such as React fiber and web assembly; and in part to developer preference. But personally, I hope to see Elm grow and even gain formal adoption for simple web apps that require performance and reliability.
Because it makes me happy.