Skip to content

Instantly share code, notes, and snippets.

@shanesveller
Last active August 27, 2020 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanesveller/7b433c5aeac813ac144a4ce86134c587 to your computer and use it in GitHub Desktop.
Save shanesveller/7b433c5aeac813ac144a4ce86134c587 to your computer and use it in GitHub Desktop.

Original writing prompt:

  1. How does Elixir really shine?
  2. What are your favorite features of Elixir?
  3. What are some things that you really love about it?
  4. What are some of the challenges of Elixir?

  1. Elixir is one of the most accessible forms of functional programming for people to get into. I think everyone with a different programming background (or no background) can find learning FP enriching. Internalizing the idea that "it's all just functions composed together" can be a big lightbulb moment, and will definitely help you write more idiomatic Elixir code.

The standard library is rich and well thought out, and it stays away from unwelcoming terminology that other FP languages have borrowed from other disciplines, such as category theory. With a few exceptions, most modules and functions are named after things that a person would readily guess with just a little bit of technical background.

Tentpole libraries like Ecto and Phoenix are some of the best of their kind.

I think the BEAM's built-in observability is largely without peer.

  1. I think supervision trees are probably the most powerful feature/paradigm of the language if you made me pick only one. The rest of the OTP abstractions, like GenServers or gen_statem are a close second. Learning them well will give you a big edge in writing and understanding Elixir code.

The breadth and depth of the Erlang standard library is also a much-overlooked asset. There's all kinds of stuff in there, including a directed-graph module, state-machines, a treasure trove of tracing and debugging facilities, SSH and FTP clients, an XML parser, two different kinds of in-memory storage, etc. etc.

  1. Mix + Hex is probably the first- or second-best language package manager I've worked with, in a close race with Rust's Cargo. It's overwhelmingly better than NPM, Rubygems/Bundler, Maven, or Go modules.

Built-in first-class support for documentation and testing as a language feature similarly puts Elixir above ecosystems where those ideals developed later or not-at-all.

If you want to do embedded development, Nerves is a really fun experience.


What I don't love, an incomplete list

The dual-language stack of Elixir on top of Erlang is a big hurdle to attracting and educating newer developers, as well as an operational complication that most other ecosystems don't have (except other virtual-machine languages like Clojure/Scala/Kotlin). The compatibility matrix is something we have to be carefully mindful of, or else stay stagnant with our choice of what versions to use, missing out on security and performance improvements.

The lack of a built-in type system is a rather unfortunate gap, and static analysis tools like Dialyzer are a very poor substitute. It is left as an exercise for the author to do things like pattern-matching on a struct or using guards to help provide defense-in-depth against unexpected or incorrectly-shaped inputs. It's easy to intentionally or accidentally skip that additional rigor, especially on aggressive timelines. Since in practice we wind up slinging around raw maps and digging around in their guts really often, we're basically doing little more than duck-typing and there's lots of margin for error. The main idea I'm speaking to here is making illegal states unrepresentable at the language level. This can prevent entire classes of bugs, user mistakes, or design mistakes.

Reading and understanding how to resolve Dialyzer errors is a hard-earned skill, because the out-of-the-box behavior has not been intentionally designed for accessibility and leaks a lot of Erlang-isms that are rather foreign to people still early in their Elixir adoption. This leads to some degree of superstition and ritual in attempts to appease it, or long lists of ignore rules for warnings you don't agree with and don't care to fix.

Error handling with tagged tuples is fairly verbose, and we didn't always have with as a special form, which helped a lot. Similar to my comments above, it's something that needs intent and attention to handle correctly, because "forgetting to check for nils and errors" is not something that the compiler will call you out for. Dialyzer might. If it doesn't, the only thing that can help is to hope that your test suite is excellent and thorough, or that your reviewers save your bacon.

Much of the initial library ecosystem pulled ideas across from other languages as people hopped the fence, and to be blunt a lot of them are not the appropriate solution for the way the language is designed and the way it behaves at runtime. You can pretty clearly pick out several that were a comfort library for Ruby or Node.js ex-pats. We didn't have anything comparable to Devise for a very long time, which José publicly called "a mistake not to be repeated", and that's a stance I personally agree with. I don't use Pow in my own projects, or things like ExAdmin.

Deployment and ancillary topics like runtime vs compile-time configuration are harder than in almost any other popular ecosystem and much harder to teach good instincts for, too. Building time- and space-efficient Docker images is nuanced, and if you configure something at the wrong layer you can wind up with code that should work and doesn't, and it's not clear why, and you don't even find out until you try to run it. The solo maintainer of the most prominent Deployment libraries, Paul Schoenfelder (bitwalker), barely even works with Elixir anymore.

Distributed Erlang and hot code reloads are frankly pretty overrated for a lot of products, and get significantly devalued if you're a polyglot organization because you have to accommodate the lowest common denominator. You can achieve most of the same user-facing uptime guarantees with horizontal scaling and load-balancers.

Umbrellas as a project structure are an extremely permeable form of "isolation" and in my mind provide neutral or negative value to one's architecture. Elixir has no concept of module-level privacy or hierarchy, only public and private functions, so if a module is loaded in your copy of the BEAM you can call it day long, even if you shouldn't be. Nothing will slap you on the wrist and you can paint yourself into corners this way.

We just can't compete by performance, except relative to glacially slow or single-threaded languages like Ruby or Python. Node and Java beat us handily in a lot of scenarioso, and anything in the C- family or Rust can do much more and with way less resources required. That's rarely a primary concern for business software, but it does exclude us from some kinds of adoption and some industries.

Speaking of adoption, Elixir's foothold at major organizations who could bring a lot of resources to bear is very low. It's hard to have a rising tide lift all boats when the language is still very niche, and largely ignored by FAANG or the likes of Dropbox, Spotify, etc. This means we're missing out on first-class support in a number of areas, and those SDKs or integrations are only unofficial, unsupported, unfunded user contributions. This really shows in the relative quality of something like ExAws vs the Go SDK, boto3 for Python, etc.

There's a lot of use-cases it's frankly just plain unsuitable for. CLIs, game development, and machine learning applications are some common examples. If you want to stretch yourself outside of the narrow cone of web development, the opportunities where Elixir is going to be a compelling choice become much more rare.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment