Ask HN: Are you satisfied with Elixir or do you regret choosing Elixir?
I'll be starting to work on a new project from next month and I've been reading all but good news about Elixir. I have talked to people in real life, most of the devs had positive things to say about it. Does anyone have any arguments against selecting Elixir? Question coming from RoR developer.
296 comments
[ 0.20 ms ] story [ 242 ms ] threadThat being said, the ecosystem is wonderful. Plug, Phoenix and Ecto are exceptional libraries!!
- Be careful about libraries. If you know that your system has to interact with X, don't assume that there is a running, idiomatic, maintained libary for X on `hex.pm`. Do a bit of due diligence. We had experience ranging from `meeh` (oauth2, graphql, http) to `pretty bad` (mqtt, swagger.)
But as other say, if you're doing vanilla Phoenix/Plug/Ecto with exactly the right kind of DB, you'll should be ok.
- Give a little though about how you'll be deploying and operating. `releases` and `configuration` haved changed in pretty big ways in the past (https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-releas...), the ecosystem is constantly in flux (`config.exs` vs `Application.get_env` vs ENV_VAR vs..) . Plus, the erlang virtual machine (BEAM) has knobs that are not always easy to play with. And if you hide it behind, say, docker containers, you'll have other troubles. Don't expect an `heroku do-some-magic` experience.
- Unless you're familiar with the Actor Model already, and your application is very simple, expect a learning curve made of:
- Finally, you're coming from RoR, so you're probably fine with not having static types anyway ; may St Isidore help you ;) There is a thing called `dialyxir`. It might help a bit ; It will disappoint a lot.Which graphql library do you prefer over absinthe in other ecosystems?
(And it's entirely possible that we simply picked the wrong lib ! Which is, still, kinda my point. Every elixir lib is "young" by virtue of the language being "young", so you can't use "age" as a heuristic for being the "right" lib. Happens for all"young" ecosystem,so not particular to ex.)
> And it's entirely possible that we simply picked the wrong lib !
This is 100% a failure of a decision-making process, not the language ecosystem. Picking the wrong library could be avoided by the smallest amount of diligence, it's pretty language agnostic.
I would also say that the "library" one is basically valid for every language. In general is always a good thing to spend some time analyzing the possibilities before importing an external lib into your application.
It has absolutely changed a decent amount over the last few years. Because it used to suck. It’s not really in flux, more like being improved because everyone knew it sucked. It’s in a much better state now and I’m very happy with it where I wasn’t before.
Also, I would say that “in flux” isn’t a proper characterization because it was always backwards compatible. Nothing broke, they just improved the experience through better methods.
What a god-awful piece of software.
I personally found that with rigorous enough usage, I don't even miss having actual static typing anymore ;)
Thinking in processes and knowing/using the GenServer abstraction is the core of Elixir programming.
So, one negative is that difficult to use without Erlang knowledge. Also some good libraries are Erlang only for compatibility/historical reasons.
But this seems like a big issue to me. It probably makes the learning curve high if u basically need to learn 2 languages.
TL;DR: very satisfied
Everything from ecto, and branching logic by pattern matching function signatures to the easy composition and cohesive community around the amazing tooling (mix/hex) and testing. Plenty more I love for productivity
But because of OTP and it's capacity to scale and distribute computation I thoroughly stay loving how deep the topic is and how there's always more to learn.
Switched from ruby to elixir 4 years ago and the only time we touch ruby is to maintain some old system.
It’s so darned easy to add functionality to an exiting app that you have to be careful of bloat.
And the functional paradigm is like second nature now. It’s hard to go back to a non functional language.
It’s hard to explain, but the end result is that we don’t reach for Redis as much as we used to.
Scala isn't exactly an imperative language. If anything it's the actor model isn't exactly easy to wrap your head around
I was talking more about Java/Python. Also Scala has inheritance and OOP. Which don’t exist in elixir has only map.Map + additional field is struct in elixir.
Function capture, enumerations, pattern matching, implicit return and tuples returning status is what confuses new people.
Most try to implement some kind of inheritance in elixir.
Actor model is in OTP. Process, supervison, actor and messages are concepts of OTP. Elixir does not have actor in in its language constructs.
There is no replacement/alternative for OTP if you want distributed systems.
Many tried to reimplement Erlang in their languages and failed miserably.
Immutability (and the resulting maintainability and predictability) is really appealing to me, though. When I try it again, I think I'll focus on educating myself around the language upfront and not just learning as I go.
Everything else about the language, Phoenix, and the tooling was really nice.
For the same reasons as others have said, I would not pick Elixir for a business, though.
In other phrasing, I’ve quickly mastered the “draw three thin ovals” portion but am now stuck at “draw the rest of the phoenix”. Speaking of Phoenix, I also agree about “mix phx.gen” to be more magical than what I’m used to.
It’s winter of elixir land. The hype cycle has died. If I have to start a project I will think twice unless it really needs distributed system.
My personal opinion - talent pool, plugins and ecosystem is kind of stagnated. First talent pool is really limited and not readily available. Very few companies use elixir in production. More hobbyists in the community than people who actually use it.
Hex has a lot of plugins which are not updated from long. For many there won’t be a plug-in. You have to write it from scratch.
In Phoenix, they are reinventing magic with LiveView.
Few guys who do elixir consulting gain most from the ecosystem.
As a developer if I have to invest my time learning a new language, I would choose rust or Go.
As a company if I have to start a project, I will check whether I have a requirement of distributed systems.
PS: I have used elixir for past 5 years in production.
Especially if your code does not saturate a single CPU core.
- testing
- Tasks (they are way better than proc_lib)
Apart from application bugs we’ve caused, it just keeps chugging along and working beautifully. It’s never once been the bottleneck in our stack.
Finding people has not been tough at our scale, and they’ve been good quality.
We are just scratching the surface with OTP. And between that and the data processing tools, we know we can handle pretty much any scale.
Deployment is nearly as crude as it was 5 years ago though. We use distillery and edeliver. We are moving to docker containers soon.
We use Exq for background jobs and have processed 75 million in a year without a sweat (most of that in the last two months). We handle 55 million requests per month on t3.mediums. We are looking to switch from Exq to Oban but I’m nervous about whether Oban’s Postgres based architecture will handle it. It’ll probably be totally fine.
That's not very impressive, you could achieve that easily on something deemed "slow" like Django or RoR
But I guess your load is not constant. How many reqs/sec can it handle in the peak time?
I'm pretty sure Java 1.0 running on an Amiga could handle that...
I just checked (because we now process most jobs in the background), we have gone as high as about 500 jobs per second in exq on that same machine (and each job typically runs anywhere from 5 to 15 SQL queries). The bottleneck is actually the size of our RDS instance. Not complaining about postgres, just saying elixir hasn't been the bottleneck for that amount. Every hour we process CSV files with 30,000 records, etc.
We only started caching things about a month ago. I have been very lazy about that since it just didn't make any difference until recently.
Again, the point I'm trying to make is that using elixir has been positively and utterly boring in the best way possible. No drama whatsoever. We've spent the overwhelming majority of any of the time related to this looking at postgres. Not even complaining about Postgres either, it's more a conversation of what's the right RDS size and IO for our traffic.
This stack has caused us very little drama.
I’d argue it’s worth the switch, but I’m highly biased!
Our workload is highly bursty. We queue about 20k jobs in the morning, which themselves queue another 100k-150k jobs. Then hourly we queue additional ones. I'm concerned about the Postgres overhead of inserting thousands of rows for jobs, each with potentially their own transaction.
With Oban, we can actually get rid of Redis in our stack (currently using it for Exq)
I think the only downside for me is having to squint sideways to figure out how to convert a complex SQL query into Ecto. I've been writing SQL for almost 30 years and I find myself dropping into using Ecto's `fragment` too often. I've been meaning to pickup "Programming Ecto" to educate myself more (https://pragprog.com/titles/wmecto/programming-ecto/).
I've done my share of RoR apps too and the nice thing about Elixir is there is very little magic and what magic there is (mostly via macros and code auto added via a `use` statement) can be seen and changed directly in your code instead of being hidden in some gem somewhere.
The final example in that post:
generates very clean SQL:We also found many times missing libraries, or found libraries which are incomplete, or unmaintained or just not well documented. Just compare the total amount of libraries in Hex, vs the total amount of libraries in rubygems. Somebody will say "but Elixir libraries are higher quality so we need less, etc,etc" as if there were not good developers in the ruby world and every elixir developer were a rockstar.
Tooling is just terrible. The VSCode plugin is crap, kills the CPU unless you disable features. There is no IDE from jetbrains. There is a plugin but last time I tried it, it was even worse than the VSCode plugin.
Also, I've read some comments where people mention "we don't need redis", "we don't need workers" everything is so much easier. That was our thinking at first. But then you realize on deployments you will lose your cache, or your background jobs, etc. So you have to persist them either in mnesia or in the database. At that point you're just reinventing your crappy undocumented and untested version of delayed_job.
Most of what you get from elixir in terms of redundancy, high availability, etc you can have that anyway from kubernetes, heroku or any PaaS.... you will need more than 1 server anyway, so...
Liveview sounds amazing until you try to use it in the real world. The most minimum latency kills the experience.
In the end, we are back to Rails and much happier. Specially now we are using all the hotwire stuff. Not fancy, and not fashionable though.
Maybe I'm missing something, but doesn't this have precisely the same latency characteristics as live view?
I do agree that some of the "you don't need redis" and similar are overblown (particularly claims about tasks replacing background jobs), but I think there is truth to these claims in the following sense: Particularly in Rails, Redis gets used not just as a tool, but also to manage shortcomings in the language / runtime. Eg: Hotwire. Ruby doesn't have a good way to concurrently manage stateful connections, so it has to fallback to Redis. Ruby doesn't have a good way to manage inter-process or inter-node communication, so once again Redis steps in to fill that gap.
There's nothing wrong with using Redis, it's a fantastic tool. Using it as a state holder for an event loop through ends up feeling extremely convoluted after you've written a few GenServers (Phoenix channels / liveview).
In the hotwire world, the encouraged approach would be to already have the html in the dom, and use a stimulus controller to show or hide it [1]. And you only issue backend calls when you really need to reach to the backend anyway (like submitting a form, or refreshing some actual content). You don't call the backend just to signal a button was clicked so that the backend removes html if you can already do that in the frontend. Hotwire is not about avoiding JavaScript, hotwire is avoid organizing it well and reducing the amount you need.
Of course you can do things wrong on both approaches, but it is what one approach promotes vs the other.
As an example of why this is a problem, see this [2] example. I'm in Europe on a fiber connection, and clicking in one of the calendar days, and closing the modal feels just so terrible....
[1] https://tighten.co/blog/stimulus-101-building-a-modal/ [2] http://expo.stimulusreflex.com/demos/calendar/2021-05-01
As noted by the parent, latency is certainly a consideration when using such tools, but there are a myriad of libs and techniques that can be leveraged to help you build great user experiences with StimulusReflex and LiveView. You just need to consider your options and architect accordingly.
Note also that the expo demos for StimulusReflex are incredibly old (a few major versions behind at this point) and were created in haste to help people grok concepts via example. They were never intended to be a canonical guide of how to best architect such apps at scale. We're considering taking the expo site offline in favor of more specific and better architected demos.
The argument against using actioncable because it doesn't scope to users by default is roughly akin to arguing against rails in production because rails doesn't even know what a user is by default.
This is essentially like saying "Dont use Rails, the controllers arent scoped to the user by default"
There are plenty of valid reasons to use websockets not scoped to a user. Also how is SR supposed to know your user scheme?
What if you have a multi-tenant app and websockets should be scoped to an account and not a user? Im honestly confused by this statement.
There are plenty of valid reasons to not scope it to a user. Having the default scoped to the most common case makes sense.
You could also use a turbo-frame here: the modal might contain data that could be expensive to pre-render in the first pass. For example, you have a list of things and you want a modal to show additional details for each thing rather than require navigation to a detail view. Those additional details might need more SQL queries that you don't necessarily want to include in your initial list render, so it makes sense to have that pulled from a separate request.
Closing the modal though should not require another round-trip to the server, that can be done with Javascript.
StimulusReflex is built on top of Stimulus. The point of SR isnt necessarily to write less javascript, its an easier way to sync client and server state and have a single source of truth.
Use SR where SR makes sense, but just because you have a hammer doesnt mean everything is a nail.
There's an open GitHub issue on LV which mentions when using live_redirect you're paying a very large penalty in page load speeds due to extra latency being introduced by LV: https://github.com/phoenixframework/phoenix_live_view/issues...
Before this issue existed my non-scientific impression was the same. There was something that felt off when using sites that primarily used LV for navigation and loading content. It always felt like I was clicking, waiting a noticeable amount of time and then the content would appear -- even with a decent ping time (< 100ms).
I never experienced that type of delay when using Turbolinks and now Hotwire Turbo with comparable ping times to a server.
I'm having a hard time finding a current example demo LV app that demonstrates page transitions using LV or to be honest any sites heavily using LV in general.
But it should be reproduceable in a demo app that uses live_redirect to transition between pages. The issue has repeatable steps and it's been confirmed as a bug by the maintainers of LV, except it looks like it's going to be tricky to solve it.
That's what hotwire gives you: an opinionated approach and a golden path to do things in a manageable way.
Seriously? This has to be a joke.
You don't really need to use alpine either if you use Phoenix with Liveview, you can probably get away with just using vanilla js if all you really need is some small functionality of modals popping up etc.
You are not Google.
Anyway I believe you really need a very special case to make Elixir shine. Obviously you can build whatever you want with it, and it's really nice, but if you are doing consulting you are going on for a hard sell. And startups will sooner or later feel the crunch of more expensive and/or difficult hiring.
Can you give some examples here. This may have been true in the past, but isn't true anymore. I can give an example where ruby is lacking: Performant HTTP libraries, Elixir and Erlang are so good in this space, look at `finch`, `httpoison` etc,. there is first class support for HTTP2 and connection pooling / persistent connections which is very hard to find in ruby.
> Tooling is just terrible. The VSCode plugin is crap, kills the CPU unless you disable features. There is no IDE from jetbrains. There is a plugin but last time I tried it, it was even worse than the VSCode plugin.
Which plugin are you referring to? Is it using dialyzer? Using dialyzer gives you so much static analysis for free as opposed to the default ruby vscode plugin. I have never heard of complaints about tooling. On the other hand, I know lots of developers who love the tooling, mix is great, iex gives you documentation with just `h String`. Plus with language server support, you have really good IDE support in vscode and vim. Look at the number of stars on the `ElixirLS` plugin here: https://marketplace.visualstudio.com/search?term=elixir&targ...
> Also, I've read some comments where people mention "we don't need redis", "we don't need workers" everything is so much easier. That was our thinking at first. But then you realize on deployments you will lose your cache, or your background jobs, etc.
Yes, you'll lose data in your cache. Caches are supposed to be ephemeral, not everyone needs their caches to be persisted. However, if your use case requires persistence, you have a lot more options than just sticking it in redis. Plus, `oban` is a high quality background processor with persistence, without the need for adding redis to your stack.
For applications where performance matters, Elixir and Erlang give you lots of tools to build a truly performant app, However, if you don't need those tools, you might be better off building it in something more familiar.
ctrl-f for "dialyzer" in this thread, and you will :)
The extension will often just lock up, either giving you no autosuggestions/errors, or refusing to remove errors that you've already resolved, and the only way to fix it reliably is to delete the .elixir_ls/ folder and/or completely close and restart VSCode, both of which require rebuilding the entire dialyzer which is a constant battery killer on laptops. This is such a frequent issue that some days I find myself having to quit/reopen VSCode at least once an hour, and despite spending absurd amounts of time trying to find a long term fix, I've never found one.
Not to mention the issue that has been open for 8+ months where ElixirLS is built using an older version of Elixir, and if you are using a recent version of Elixir for your project (anything newer than 1.9), some of the core functionality of ElixirLS just breaks (autocomplete and hover tips). That issue alone makes me want to ditch ElixirLS (and likely Elixir) entirely.
https://dashbit.co/blog/a-new-authentication-solution-for-ph...
That said, the community is moving towards phx_gen_auth which jose himself wrote and will come out of the box with 1.6
Once your database becomes sufficiently complex you start writing a lot more sql.
ActiveRecord's way of chaining scopes to combine multiple scopes is brittle. You can't customize the conditions. Ecto solves this by giving you composability. You can build on top of your earlier commands. If you combine this with pattern matching in the functions or Enum.reduce you have an extremely powerful and a flexible query engine.
Ecto also relies on the strengths of underlying database engine rather than treating it as a black box. If you are dealing with 100s of tables Rails way of polymorphic design doesn't give you referential integrity. I like Ecto's recommendations: https://hexdocs.pm/ecto/polymorphic-associations-with-many-t...
I wrote a bunch of Erlang and Elixir tooling before I realized the most useful projects were thrown over the wall FOSS codebases.
If it's stupid and it works, it's not stupid. Fashionable is for runway models.
I am using VSCode too and the experience is not as good as with Typescript/Javascript but is not that terrible either. I would compare it with Rust's experience, so far.
> Also, I've read some comments where people mention "we don't need redis", "we don't need workers" everything is so much easier. That was our thinking at first. But then you realize on deployments you will lose your cache, or your background jobs, etc. So you have to persist them either in mnesia or in the database. At that point you're just reinventing your crappy undocumented and untested version of delayed_job.
Of course you need _other_ libraries to achieve some of those things. You do not have a queue built it but there are some very good tools like Oban https://github.com/sorentwo/oban that are basically doing what Sidekiq does, just relying on the main database you are already using. There are also very good libraries for caching that rely on ETS and that simply replaces what you could do with Redis.
> Most of what you get from elixir in terms of redundancy, high availability, etc you can have that anyway from kubernetes, heroku or any PaaS.... you will need more than 1 server anyway, so...
This is partially true. The ability of BEAM to cluster and execute processes inside the cluster, internal communication between actors and so on are not _just_ achievable with k8s, not without a good added complexity.
>> I strongly disagree (doesn't use IDEs)
??
Yes, the extension uses a ton of CPU for 15 minutes the first time you set it up. It’s building a giant database for type information to give you type checking and hints.
It does this ONE TIME when you first set it up. It explains this. It’s really not a big deal. I’ve found the extension to be great, I’m not really sure what you are referring to there.
I love Elixir and the ElixirLS extension is the best I've found so far, but that's not saying much. The extension has enough issues with it to make it very frustrating to work with, and even enough to make the entire Elixir experience not worth it in many cases.
> Tooling is just terrible. The VSCode plugin is crap, kills the CPU unless you disable features. There is no IDE from jetbrains.
This is a bizarre criticism of Elxir the language. There are a lot of developers out there who prefer a more lightweight editor and do not want the bells, whistles and crutches of a heavy duty environment. I would never complain about a language just because an IDE does not exist for it.
Tooling in Elixir is really some of the best I have ever used as far as the consistency had with mix, hex, hexdocs, etc...
> Also, I've read some comments where people mention "we don't need redis", "we don't need workers" everything is so much easier.
You can still use all the same battle tested tools with Elixir... but it is true that there are lots of situations where those things are genuinely not needed thanks to some of the things that come out of the box on BEAM.
Right, but...there are also developers out there who do want those things.
It feels very dismissive to dismiss a complaint like that as "petty" just because there's a set of people who don't have the same needs. I feel like it's a very useful piece of information about the language that doesn't feel at all petty to me.
If you don't want the IDE-like tooling, then you can read the comment and think: "ah, they want more tooling than I do, so this critique doesn't apply to my use-case". If you do want that style tooling, then you might read the comment and think: "Ah, that's good to know. That applies to me, and I'll definitely take it into consideration".
Someone having priorities that are different from you doesn't seem "petty" to me. It just seems...like different priorities.
I think their complaint is valid, especially in light of their finding it difficult to find experienced coders who are in their salary range.
New coders can lean heavily on a good IDE to help: code completion, breakpoints, step wise debugging, one click links to the docs, easy code navigation, and more. Senior coders can and should use the editor of choice, but a great IDE makes a world of difference for a junior or even intermediate coder.
We're in a similar boat. I don't know the full history of how we came to use Elixir, but I would wager that early on in the company, someone really loved it and evangelized it. But now, we don't have that many senior people who know it well. I can count the ones I talk to regularly on one hand, and once I run out of fingers, I'm probably sixth place runner up, which makes me very uncomfortable.
That's why I wouldn't suggest Elixir. Unless you're incredibly sure you won't have significant churn for the lifetime of your business, or have money to burn specifically hiring Elixir folks, I wouldn't recommend it.
Adopting Elixir, like Clojure or Haskell or any of the more esoteric languages is a commitment. Don’t do it if the benefits aren’t there or you aren’t willing to make the investment.
That's very true. But it's still something to consider, especially for OP who's choosing a new language for a new project.
I encounter a lot of devs, especially experienced devs who pick it up very rapidly compared to what I've ever seen with JavaScript, ObjC, Rust or other most other languages I've spent time with.
The language itself is very different from the C-like imperative languages most everyone knows, so there's a bit of a ramp-up from that, but for basics, people can be released into the codebase within a month.
But when things go wrong, or when you need to do something more complicated, things get difficult. It's much more difficult to google around for solutions. When Dialyzer is spitting out strange errors, hunting down their causes and solutions is challenging without someone who understands it around.
I feel like 99% of Typescript/Ruby problems have been encountered and enshrined in Stack Overflow and the like, but Elixir doesn't have that sort of volume.
It's a fun language, and worth learning, and I played around with it in a side project, but quickly started running into conceptual roadblocks when it came to (no surpise) the GenServer stuff, and how to actually spin up a stable service that does things in some given way. I probably could have stuck with it, but at that point it felt more like toil than joy, so I decided I'd rather do something else with my time. I'd be fine with going to work for another company working with Elixir, provided that there were people there who can swoop in from above and help me solve my problems.
This whole thing sounds very much "ruby good, elixir bad"
Yeah, last time I tried to get a hang in Erlang I felt it was trying to solve stuff that is already being solved by my infrastructure in a language agnostic way.
Not to argue your point, though.
Asking because I'm just curious about the comparison between the two
I used Erlang for 5-6 years in a production environment in a deployment that wasn't the world's biggest, but did seriously need clustering because we needed more than one machine to handle the load, not just redundancy. This comment leads to my core observation about the entire ecosystem for anyone considering using anything in the Erlang ecosystem.
Erlang started in the late 1990s. Joe Armstrong was brilliant, and I would imagine was also surrounded by some other very smart people whose names I do not know. Despite what I'm about to say, let nothing I say be construed as disrespect for either the language or the people making it.
Let me set some context here. C++ ruled the day, and still looked to inevitably replace all C. Python, Perl, and the entire dynamic scripting language category had just been invented. Java was in the news, but not on your computer yet. Haskell... pre-monadic IO... had just been standardized somewhere in here. Threading was possible but was every bit the nightmare it has been presented as, and you had to be careful spawning more than single-digit threads because of the resource consumption. Open source was just beginning to be A Thing... that is, it had existed in its embryonic form for decades, of course, but it was just beginning to cohere into the Linux world we know now. Machines were still measured in the hundreds of megahertz and the single-digit megabytes of RAM.
Erlang was a far more radical departure from anything else in this era than it is today. There was a lot of academic work on this stuff, and there was a lot of very specialized high-end work on multiprocessing, but it wasn't being done by "mere mortals" and it wasn't very simple. Erlang's designers looked out into the world, and what they saw was a huge jungle, where what maps we had had huge regions that just said "Here There Be Dragons". And they started in, hacking and slashing and slicing their way through, guided by a few intuitions and a whole lot of engineering firepower.
And they largely succeeded in creating a settlement in the wilderness. They grew and managed to pave over the hack & slash paths into roads, built a community, built a pretty incredible VM, built an ecosystem around them. Let this accomplishment not be underestimated; this is a land that had eaten many others in that era.
However, sitting here in 2021, this is no longer a wilderness. Much of the area has been razed, highways driven through it, McDonald's by every exit, and millions of people living here in various bustling metropolises.
And I think what we've found is that where Erlang plopped itself down is OK... but not more than that.
As a consequence of its isolation, Erlang bundles a lot of things into itself that just didn't exist back in the day. It bundles in messaging passing and a network-aware message bus, almost literally decades before anyone else even thought of a "message bus" as a distinct product segment. (i.e., they existed, you could find academic discussion and some early passes at it, but it wasn't really a distinct category yet.) It has a threading environment. It has these "supervision trees" idea which are cool. It has immutability at a time where Haskell was even crazier than it is now. It has a mechanisms for bundling and distributing applications....
One of the reasons I make a bit of an accusation that the people bedazzled by Erlang/Elixir may be not very experienced is that a common take away from the community I have repeatedly noticed is the belief that if another language doesn't have exactly what Erlang has, warts and all, it doesn't have what Erlang has. Thus, I see in many other languages various attempts to port the exact thing Erlang has out into that other language, when in fact the other language already has solutions to that problem, even if they aren't exactly how Erlang solves it. [1]
While I personally exceedingly strongly agree that the Erlang-like threaded approach is vastly superior to manually explaining the asynchronous-ness of your code to the compiler, it still remains the case that "async/await" largely solves the problem of handling millions of threads at a time in those languages that support it, and even if that particular element of it is inferior to Erlang, it will be compensated for by the wide variety of other things that are superior. Just because it isn't exactly like Erlang doesn't mean it's not decent solution. Moreover, my exceedingly strong agreement is also, like, my opinion, man, and there are plenty of people who disagree with me and consider async/await superior, and they would account this as simply being better than Erlang/Elixir.
In the 1990s and the 200Xs, Erlang had solutions to problems that no other language had solutions for, or where Erlang's solutions where clearly head and shoulders above. In 2020s, Erlang no longer has anything that it uniquely has the solution for, even if the exact Erlang solutions don't exist elsewhere.
[1]: I wrote about this recently at https://news.ycombinator.com/item?id=26833616
On one level yes, they have constructs that you can use to accomplish some of the same goals as the tools Erlang provides, but some people (me) like the specific programming model that Erlang provides for dealing with these problems. The concurrency characteristics of the systems I hack on every day would be much harder to model without GenServers. It's not particularly surprising that Erlang is not the only way to solve a problem in software engineering, there is no silver bullet.
I've written much more C# than I have Elixir in my time as a developer and the fact remains that I prefer functional programming and I like using OTP. I don't see what the "wide variety of other things that are superior" are in languages that aren't Erlang. The ecosystem is stronger and the tooling is better, but those are not intrinsic to the design of the language. Tooling kind of is, with static languages, I suppose. Lack of static types are the one thing that bother me a lot about Elixir.
Erlang was at one point the only way to get this sort of structure, though.
Now it isn't. Multiple languages like Go have lightweight coroutine-y type things; if the isolation isn't perfect, you can fix it with programming discipline. There's also Pony, and Rust which has its own thing which is in its own way far stronger than what Erlang offers. Async/await languages are very amenable to setting up lots of little process-like things, to the point that arguably it's the only sane way to structure your programs in such languages. There is a real change in Erlang being almost the only thing on offer, to merely being one choice of a whole bunch and no longer being impressive enough to justify the lockin. (Not that Erlang uniquely has lock in; all languages have lock simply by virtue of the fact that once code is written in X you can't just change it to Y one day. But it's not worth the lock in anymore.)
It still is though! Isolated processes with asynchronous, dynamic message passing requires really heavy libraries in most other languages. Rust is completely different, I can't fathom why you'd compare them in this way.
Message buses solve the message passing requirement. You can get a whole range of solutions from "just sling vaguely JSON stuff around" to "here's a whole strongly typed protocol", with a whole array of options on persistence, clustering, language support, etc., a whole array of options around how synchronous they are and what guarantees they provide, rather than Erlang's one exact solution,
You're kind of demonstrating my exact point here... it isn't that the exact Erlang solution shows up in other languages, the point is that the problem space that Erlang covers is now covered in plenty of other languages, in all sorts of ways, many of them better for some task than Erlang's one point in the space that was selected very, very early in the exploration process. This was not the case 20 years ago. It is now. And I don't particularly think Erlang's coverings of the problem space are the best. People not already deep into Erlang aren't evaluating whether or not some solution is exactly like Erlang, they're evaluating whether the solution is a good solution on its own terms, and once you remove the "must be exactly like Erlang" clause there are a lot of better choices for every element of Erlang, except arguably, the integration of all of them together.
You can write distributed coroutines in assembly with enough programming discipline. What is interesting in a language is not what it lets you do, it's what it prevents you from doing.
Just taking on some random stuff:
> Trivial pattern matching is replaced by this, and to be honest, non-trivial pattern matching is probably a code smell if not an antipattern; if you're reaching three levels down into a data structure
If the data structure is being accessed in a module to deal with it specifically then that is part of the knowledge of the module. And you can't escape that with or without pattern matching, since you'll have to codify those things in code anyway, be it long chains of ifs, switches, or whatever, you'll always have to write the code with the same level of knowledge about the data structure to express the same conditions?
> Modern Erlang performance is meh;
This I'm not sure. I've seen libs written in elixir beat libs written in C++ and they had to start a VM to be run. Obviously there will be plenty of stuff that a low level language can do more efficiently and faster I don't think anyone disagrees with that - but I think sometimes people sell erlang and elixir's speed short.
> but the entire gen_server set up, with its initialization phase and its three proscribed ways to call it and the need for an explicit "gen_server" module type are largely the creation of Erlang in the first place
I haven't seen any language with the concept of an independent program, that has a synchronous interface (mailbox), but can be modelled entirely asynchronously, and can't block whatever running loop. (and I read your other reply and thread). There's also a reason why some things have a particular way of being set up inside the beam, and that's mostly related to the guarantees it provides and with the fact that processes are transparent across nodes. If you take out the impossible to block scheduling and network transparency you'll be of course able to spin up something similar to a GenServer without the same "ritual" - but it no longer has anything to do with the former perhaps except its API?
> A modern message bus like Kafka or the dozen other choices doesn't impose an implementation language on you, nor does it impose that implementation language being the only one.
Given that you can have any socket, tcp or not, and easily handle it, this doesn't seem to be a fair point. On kafka or any other message bus the implementation language will be that of the sources and sinks communicating with kafka? Or you just write kafka and the other programs wake up as insects in a bed?
> Mnesia, a clustered DB was a neat idea, but in 2021 is so poorly featured and unreliable it doesn't even qualify as one anymore; nobody in their right mind would bring up an Erlang cluster just to back some other language's code to Mnesia as a database
I haven't used mnesia at scale and indeed heard people complaining about it, more than once so there's probably some issues there - having said that, in terms of similar db's (KV based and not SQL,etc) even db's like MongoDB and such need expensive and usually paid managing solutions right? And can still loose data unless you really cover your redundancy?
> But in 2021, you can do not just "better" than Erlang for all of them, you can do much better.
Could you tell us succinctly how? As a, usually, solo developer I would be really interested in knowing, and I'm not asking sarcastically.
I also don't understand how it's difficult to integrate Erlang with other things, it basically has a standard lib very accommodating of sockets and inter process communication through STDIN/OUT, with effective monitoring on top.
There are other options. In object oriented design, using the loose definition of "anything with 'methods'" (thus, including Go and Javascript even if they don't have "inheritance"), if you want to know something, you ask directly, using a method for that case. It is the difference between "account.getBill(lastMonth)", where you load the logic into the account object to fetch bills from different times, and pattern matching where you write into the code taking the bill deep assumptions about how the account is structured, such that when refactoring the account you have to go change all its consumers. Note I am only criticizing deep pattern matching here; pattern matching in general I like, even if I can live without, but in Erlang terms, if your patterns look like
you've gone off the rails."I've seen libs written in elixir beat libs written in C++ and they had to start a VM to be run. "
There's almost always some microbenchmark a slow language can beat a fast language in, but that doesn't make it a fast language. You might want to dig into that "elixir lib that beats C++", because either the C++ was really bad or the elixir lib is actually written mostly in C(++).
"I haven't seen any language with the concept of an independent program, that has a synchronous interface (mailbox), but can be modelled entirely asynchronously, and can't block whatever running loop."
Yeah, but when you're talking about a 30-40 year programming language, that's not a necessarily a compliment. Nobody else has seen fit to exactly copy it, because we've found better things to cover the problem space than that.
"Given that you can have any socket, tcp or not, and easily handle it"
There is a lot more to being a message bus than just having a TCP socket.
"I also don't understand how it's difficult to integrate Erlang with other things, it basically has a standard lib very accommodating of sockets and inter process communication through STDIN/OUT, with effective monitoring on top."
That is not "easy to integrate with", that's table stakes. What I'm referring to being hard to integrate with is how you tend to be stuck in the ecosystem. It has monitoring, sure... but in 2021 it's a very quirky monitoring that doesn't integrate with any of the rest of what has developed. You can, in theory, connect to an Erlang cluster without Erlang, but in practice the requirements of being "an Erlang cluster" are so specific, with its own quirky term format that isn't quite JSON or quite anything else, is so difficult as to not be worthwhile. Once you've taken the time to speak to a non-Erlang message bus, use a non-Erlang database, wrap your Erlang code in K8S or some non-Erlang manager, use Erlang to hit HTTP APIs, speak to non-Erlang monitoring systems... why are we using Erlang again?
> There's almost always some microbenchmark a slow language can beat a fast language in, but that doesn't make it a fast language. You might want to dig into that "elixir lib that beats C++", because either the C++ was really bad or the elixir lib is actually written mostly in C(++).
Usually it's microbenchmarking that doesn't flatter elixir/erlang - and stampede problems where you can get away with just brute forcing - eg. parsing directly to output a bunch of files where the output is a file(s) - but there, if you want then to build usable metadata through all of it things change fast - writing a correct program in any of the fast languages that has the same ergos for keeping usable information throughout and is at the same time easily changeable, can interleave information in parallel with pretty amazing monitoring, and doesn't require years of training, is quite a different thing.
I know it doesn't use C++ because I wrote it and I know how much more it does as well, though I'm not here to convince you.
> Yeah, but when you're talking about a 30-40 year programming language, that's not a necessarily a compliment. Nobody else has seen fit to exactly copy it, because we've found better things to cover the problem space than that.
Well, sure, I just think it should not be written as "cover" the problem space, is more "making holes" in the problem space.
> There is a lot more to being a message bus than just having a TCP socket.
Given that your argument was:
> A modern message bus like Kafka or the dozen other choices doesn't impose an implementation language on you, nor does it impose that implementation language being the only one
I'm not sure there's any more esperanto than tcp/sockets, which comparing to any other language are a joy to write, use, monitor, and deal with.
> You can, in theory, connect to an Erlang cluster without Erlang, but in practice the requirements of being "an Erlang cluster" are so specific
This sincerely... How do you connect to other clusters in other languages (if they're even able to set up meshes)? You don't. How do you solve it? With clustering solutions. How is it that a runtime that can be put inside those same clustering solutions, by the same process, but has a zillion more functionality can be worse than those other solutions regarding that?
> Once you've taken the time to speak to a non-Erlang message bus, use a non-Erlang database, wrap your Erlang code in K8S or some non-Erlang manager, use Erlang to hit HTTP APIs, speak to non-Erlang monitoring systems... why are we using Erlang again?
Yeah, because they all speak something else than http/tcp and we know erlang is not really made for handling sockets and can't do http.post().
Really appreciate the effort that you've put into this post but it's 99% saying you can do better but without examples of stacks that would be better. For those who don't have your knowledge could you provide some examples?
Rather than specific examples, let me provide terms to google, as these are now such rich spaces that even a list of examples would be impoverish. Google "message bus"; there's half-a-dozen solid production options you can deploy yourself, and all the major clouds have at least one option, often more. Pervasively use a message bus in your architecture and you can't hardly help but end up programming in a very Erlang-esque fashion.
"Database"; no longer is an SQL database over a socket your only option. Some databases even combine a bit of message bus functionality, like Redis, or Postgres.
Restarting and reliability: Process-level restarting of much smaller processes has become popular with things like Kubernetes, Docker, and even just plain ol' Systemd. (This is one place that I will assert that systemd is better than the init.d-based system, which had a solid story on how to start processes but a very ad-hoc one on restarting failed processes.) Internally, consult your favorite language for monitors or restarts within a process; I've got one for Go called suture. Getting an idiomatic restart into a language depends on tons of details and I don't have a good sense of the options across dozens of languages. Note this only really matters if your language and program is handling a lot of things at once in a single process, which is not always the case.
That's most of what would matter, I think. Most of the rest of Erlang like "pattern matching", well, do whatever they do in your language. Which may be pattern matching.
https://github.com/actix/actix https://github.com/akka/akka
http://erlang.org/pipermail/erlang-questions/2014-June/07979...
In fact Elixir does nothing for this. It is true that in Erlang, you cannot rebind a variable and must use a different variable name for every assignment. In Elixir you can rebind the same variable, but the data is just as immutable as in Erlang, and the rebind is only visible in the current scope, which trips up new programmers not used to it.
We faced similar issues to you, bad tooling, weak libraries, and very few knowledgeable devs.
Finding people to hack on elixir was extremely difficult, and likely the driving force that killed the project. Having an inexperienced team who had no experience with many of the language features elixir offered created a very difficult to work in code base.
This killed productivity, so management threw resources at it. Code quality dropped further when we were no longer just a group of noobs interesting in hacking on elixir, and became a group of noobs begrudgingly hacking on elixir.
I feel pretty scared from the experience, and wouldn't be interested in using elixir professionally again. Which is a shame, it can be a nice language to write, and some of its tooling like hex is quite nice.
For what is worth, that's one of the reasons why Elixir exists in the first place. I find the Erlang VM a fantastic platform and there is no reason to restrict it to high availability/distributed scenarios. Elixir developers should be productive and write performant and maintainable applications, even if at the end of the day they are just pushing to Heroku. Especially if you are coming from another dynamic language, such as JS/Ruby/Python.
As I like to say, in the "worst" case scenario, you can use Elixir as you would any other language, and that's completely fine!
If you want/need to go deeper, the abstractions are there to do so, and that's the difference compared to K8s: it is happening one layer inside. The high-level principles of redundancy and availability exist at the library level, so you can look at your connection pool, message queue, http clients, and find the same ideas there. Another area where K8s directly complements Erlang is in helping establish clusters. Overall, they are rather working together rather than stepping on each other toes. I wrote more about this here: https://dashbit.co/blog/kubernetes-and-the-erlang-vm-orchest...
Erlang (and by extension Elixir) is a concurrent language. You use it when you have non-trivial concurrency challenges. When tackling the right problems, the language shines.
Implementing a simple CRUD application with Elixir is overkill, because you can make your servers stateless and delegate most of the concurrency problems to the database.
At my previous job we had *ALL* of them. Each service or project you opened you could guess the date it was started because of how it was built. Rails -> Node -> Elixir -> Go -> Next.js and the frontend backbone -> ember -> react (redux -> mobx -> rxjs -> [some other random state lib] -> just context! -> recoil) -> typescript and probably by now they're already using whatever the new trend is by this morning. It is insane.
None of the companies needed any of the performance provided by this tools, in fact, they all struggle with developer productivity more than any other metric, and every time they switch to the new shiny toy, productivity takes another huge cut. But hey, people get paid anyway at the end of the month, so let's keep playing.
https://github.blog/2020-08-25-upgrading-github-to-ruby-2-7/
That's my main blocker, proper JetBrains IDE support just makes everything so much easier. I need a debugger.
I tried the plug-in, and kudos to the author but it did not work for me. In the end they're only one person so of course they could not test every single configuration
-
I wonder why JetBrains has not put their weight behind it.
> We built a few services
So you never really committed to it in the first place. Also, this complicates the deployment problem.
> after a few years some of the original people that introduced it left the company
Probably left for a company that actually committed to Elixir. :P
> and it became very difficult to hire for
In a world where everyone is remote and where 10 Elixir people apply to every job, this product must have been pretty unappealing
> New hires were either people wanting to learn (so we had to spend a good bunch of resources into teaching + end up with a system built by noobs to the language) or very expensive developers with a lot of experience in erlang and elixir.
"We didn't want to pay employees their worth and instead bitched about what we couldn't get without hiring those employees"
(Why couldn't you hire an assortment? One experienced guy and a couple noobs?)
> We also found many times missing libraries, or found libraries which are incomplete, or unmaintained or just not well documented
Alright, fine. Sometimes you have to "roll your own" in this space, still.
> Tooling is just terrible. The VSCode plugin is crap
You should not use the word "tooling" here because VSCode is not an IDE, Elixir should not require an IDE, and moreover, Elixir should not be judged because "there is no good IDE for Elixir". "Tooling" should refer to the support libs and tools that ship with the language, all of which are excellent.
> At that point you're just reinventing your crappy undocumented and untested version of delayed_job.
Spotted the guy who never heard of Oban https://github.com/sorentwo/oban Benefit of the doubt: Perhaps it didn't exist yet.
> Most of what you get from elixir in terms of redundancy, high availability, etc you can have that anyway from kubernetes, heroku or any PaaS
This is entirely missing the point. If a bug or runtime error crashes your Ruby interpreter, you better have another one ready to go from a pool (because Rails stacks can take a while to load), and then you better not exhaust that pool! If such an error crashes Elixir, it just restarts the process, which only takes a millisecond because forking an immutable language's state is trivial compared to a mutable language's state.
> Liveview
I actually haven't played with it much yet so can't comment
> In the end, we are back to Rails and much happier
"We can underpay cheap devs again"
You also repurchased entire classes of bugs that are impossible in Elixir such as: mutation bugs, monkeypatch bugs, and concurrency bugs (just forget running your test suites in parallel). Also, these are literally the hardest types of bugs to fix (nondeterministic behavior), and will likely cost you more in the long run than any differential salary you balked at (I have spent entire months debugging something in the Ruby space, you'll remember my comment once this bites you in the *** one day).
That's really inaccurate, not everyone is remote and looks like post COVID most companies will be hybrid office/remote. So hiring is still a problem, and getting hired is a problem for people who don't want to be 100% remote.
> "We can underpay cheap devs again"
I don't think Rails devs are particularly cheap though I'm not sure if that's what you meant.
> mutation bugs, monkeypatch bugs, and concurrency bugs (just forget running your test suites in parallel)
Rails comes with parallel testing since Rails 6. As for concurrency - I know that Gitlab moved to Puma (a threaded webserver) and tons of huge companies use Sidekiq which is threaded as well. I'm not saying there are never any problems but it seems to work well for many many companies.
It's only a matter of time until you hit something as confusing as https://elixirforum.com/t/dbconnection-connectionerror-pid-x...
Heh, the core system I put in place at work 3 years ago and is updated daily has never gone down once. Isn't that the point of the BEAM and supervision trees?
It's different, requires to learn a new skillset, but I wouldn't call it "challenging to keep running."
Previously I wrote Scala for a few years, then RoR for a few years before that, following about a decade of Perl. I recommend you try Elixir.
Anyone complaining about a talent pool is focusing too hard on prior Elixir experience. We hire backend engineers and teach them Elixir. This has worked out great so far, especially now that we're a fully remote business and are no longer as constrained by geography.
Five years ago I spent time learning Elixir on the side thinking I was investing in an upcoming technology that would pay off for me financially. I eventually built some services in Elixir at work and really enjoyed it. They performed well but the language didn’t catch on with the rest of the team.
However, five years later the mainstream languages (Ruby, Go) are still paying more. I still get recruiter messages for Elixir jobs offering less than market value but I haven’t used the language in over a year.
> Sure someone could learn it, but we’ve got more important things to do
If they can't give you 2 or 3 days to learn a language then they don't regret it that much I guess.