Elixir 1.9(elixir-lang.org) |
Elixir 1.9(elixir-lang.org) |
I'm not affiliated with the language but I just want to throw out that I've been around since the Geocities era as a web developer and I haven't seen a tech stack or platform this well thought out and so in tune with what makes developing (and testing!) modern web applications an absolute joy. There's really something special here. I see myself sticking with it for 10+ years with 0 regrets.
The best part about it is everything is ready today (and was before 1.9 too btw).
If you only need basic functionality and absolutely have to use Phoenix, Pow is a good choice:
Personally I haven't found auth to be a problem in Phoenix and I have lots of experience with Rails (and using Devise).
You can put together an iron clad user registration + auth system in about 150 lines of app-level code (not including lines of code for the template forms, but with Devise you would end up customizing your own forms anyways). This includes registering new users, authenticating by email + encrypted password, logging in, logging out, session management and having the idea of a current_user in your system, along with a way to restrict routes, controllers or actions to only logged in users. The essentials basically.
I would much rather manage ~150 lines of code in my own project that I fully know and can easily customize than pull in something like Devise which is 6,000+ lines of Ruby and have to configure / override a number of things.
Everyone has their own opinions but I would much rather spend my time developing features for my app than trying to figure out how to customize a massive third party dependency.
Personally I'm rolling with a magic link authentication system in my Phoenix app and it was also around 200 lines of code to implement everything, including configuring and sending the emails out. There's no external libs beyond Phoenix to get it all working (except for sending the emails out, in which case I use Bamboo which is a lib dedicated to sending emails).
In one day I can quickly get something up and running with Guardian and have full control over how my app does authentication. Password reset and confirmation are easily done with Phoenix.Token without a need to touch the database.
Also, authorisation is a breeze on Phoenix. Take a look at Canada: https://github.com/jarednorman/canada - and it is only 10 LOC.
I agree, but it's not unique to Phoenix. In the PHP world the otherwise excellent Symfony framework is only starting to add generation of authentication/authorization, and email confirmation and password reset remains the developer's responsibility.
I see two reasons for this.
1. Frameworks see themselves as building blocks, not RAD systems. When the core framework developers are (mostly) working on APIs they don't see the need for approval/password reset. After all "you'll issue the user with a JWT so who cares about the rest (shrug)"
2. Flexibility to auth with anything. Apparently not bundling a system makes it more "flexible".
I believe (1) to (a) be shortsighted and (b) we need RAD frameworks, of higher-level flexible components that can be bolted together.
I believe (2) is wrong, and bundling standard auth flows does not reduce flexibility: you implement your own if you need something else.
Maybe for AuthN, but AuthZ is pretty far from the business of a web framework. The framework authors seem, for the most part, to be avoiding first-party endorsement of secondary tooling that doesn't deeply benefit from being first-party. I find it super refreshing coming from Rails where it is either the golden path or good luck.
A lot of the early AuthN libraries seem to target the controller layer, but I've found, having worked on a couple of sizeable Phoenix production codebases, that AuthN at the app domain layer works better. Regular use of channels as an alternate entrypoint to web request controllers means that controller based helper libraries are less valuable. Once you push this logic inside the app domain, it really isn't the business of Phoenix to know or care about it, since you're just working in plain Elixir, probably with some sort of Ecto struct to represent identity.
But recently I read the Programming Phoenix 1.4 book (written by the authors of Elixir and Phoenix) and it really filled in a ton of gaps and made so many things click together. I can't speak highly enough about it. In fact, finishing that book today is what provoked me to write my comment here. The book might be exactly what you're looking for as like 5% of the book focuses on the super basics and a toy example, but 95% of the book focuses on building up a pretty real app and layering on features as you go. It's also up to date and feature complete even though the book is labeled as a beta release.
By the time you finish the book, you exercise a ton of interesting things along the way (including how to take advantage of Erlang's VM to build features that would be 100x more effort and probably 10x worse in other tech stacks). You definitely don't just build a simple todo app. I don't even want to say what we build in the end because it feels like a spoiler. I only say that because when I hit the last section of the book I had no idea what was coming and was blown away by what it covers. It literally made me smile for hours just thinking about how badass everything is when it all comes together.
I've also put up about a hundred screencasts at http://youtube.com/alchemistcamp.
https://pragmaticstudio.com/courses/unpacked-full-stack-grap...
These have been my go-to that seem to fit what you're describing. I hope this helps.
Another example is: https://github.com/AltTracker/alttracker
It's an implementation of fully functional site. The beauty of Elixir is that the code is very readable.
Trust me, if somebody puts that in Go, Rust or OCaml -- I'd ditch Elixir tomorrow.
The BEAM VM is just too good. You can have thousands of smaller tasks running in parallel and nothing lags until you hit the physical limits of the hardware -- which 99% of the apps never do.
Anyways, this subject occasionally comes up for discussion in the community. This post sums up my thoughts fairly well: https://elixirforum.com/t/static-vs-dynamic-typing/9824/3
I currently use Windows (where I spend 99% of my time in WSL) and everything runs great.
I've used the Windows installer[1] and have had no issues.
I know it is very similar to Ruby.
I wish there was a ElixirC syntax that would also compile to Earlang.
* Capitalized variables names
* use of comma, semi-colon, and period as clause delimiters
It's interesting, but I think that Prolog-style syntax is probably the easiest to read if you don't have syntax highlighting available (though I don't think there is as much of a difference with syntax highlighting).
Elixir has been second or third on my todo list for a long time. With the scary news from Vue, it might have just jumped the queue.
http://rvirding.blogspot.com/2008/01/virdings-first-rule-of-...
Do you have a link?
Something striking from the release announcement:
> As mentioned earlier, releases was the last planned feature for Elixir. We don’t have any major user-facing feature in the works nor planned.
Part of me is a little alarmed—no one wants a language to stagnate. José makes it clear that this doesn't mean it's the last version:
> Of course, it does not mean that v1.9 is the last Elixir version. We will continue releasing shipping new releases every 6 months with enhancements, bug fixes and improvements.
Elixir is remarkably extensible. When I was first getting acquainted with the language finding macros with all the expressive ability of a LISP blew my mind. Making powerful macros a core part of the language meant that the language could grow. That in my mind is the most important part of any language's long-term viability.
Fantastic work all who contributed. Thank you for making a fast, functional, and fun language to work with!
Distillery has helped a lot for sure, so it's nice that it's basically been rolled into the base elixir distro.
If I'm writing an application that I'll only deploy myself I can of course match the environments exactly. But what about if I wanted to distribute an Elixir application to end users that might install it on all kinds of different operating systems and distributions? Do I have to build a release for OS/architecture combinations like Linux/64bit, Windows/32bit and similar? Or do I have to match each Linux distribution and windows version exactly? That would make it rather impractical to use Elixir to write software you give to other people, and don't deploy yourself.
Of course when I looked at this Distillery was the standard way to deploy, but it doesn't look like this part is any different with Elixir Releases.
The other concern is packages with NIFs (native code) and they would need to be changed if they don't support cross-compilation yet.
I believe Nerves, which is an Elixir framework for embedded, does a good amount of cross compilation too but they have a more controlled environment.
Can anyone provide a good place to start on where to get started with Elixir?
I tend to learn by working on stuff and not just reading etc, maybe a step by step in elixir? I am going into the literature now as well
Thanks in advance for any help!
> From experience, we could deploy to a 70-node cluster in something like under 5 seconds by doing it in parallel. If you want to rotate your infrastructure... your deploy could be taking from 5 minutes to an hour depending on how fast the connection draining can be done.
https://soundcloud.com/elixirtalk/episode-145-feat-fred-hebe...
Even without the compilation and configuration stuff, it's easier to put the release bundle in something basic like an alpine image, rather than keep docker image versions and app in sync.
Notably, Elixir's release implementation does not support hot code upgrades. I use upgrades all the time, and won't be trying out Elixir's releases until this shortcoming is addressed.
- too much magic
- slow and painful to scale
- too much metaprogramming
- no compilation, makes it easy to hide syntax errors
- concurrency is absent
- frameworks like rails are too bloated
All these are addressed on the elixir platform, and there are more pros:
- immutability
- mostly purely functional
- well defined modules with good documentation
interested to know, how the releases feture going to affect the way we deploy elixir/phonenix apps in production ?
It can handle stateful things and distributed systems far nicer, with a programming model that is really adapted to it.
In the sense of the language itself, I do. The language community should keep growing, libraries, frameworks, etc., but I'd like to see more languages qua languages declare themselves "done" sooner and more often, or failing a sudden cutoff, start seriously raising the bar on the next "new feature". There's a lot of good languages out there that were great in years 5-15 and then choked on all the features they kept adding to the core language.
I used to be so happy when there was a new Java version every year, something new is good, something better is good, some hype is good. Now I am getting old I just want to use the same tools for the next 15 years. I mean it is not like most of the problem we are trying to solves were limited by the languages itself. ( Mostly the ecosystem around it )
Yes, of course 99% of the book is probably still relevant and all, but I believe it is important to brand a language as having reached maturity and stability.
However, in terms of new features, we have already been slowing down over the last releases.
That they are willing to put that in a primary book title is pretty bold.
It was clear to both Paul (Distillery author) and the Elixir team that Elixir releases were going to be a subset of what Distillery provides. There is a good chunk of what Distillery does that is well established and that's what we streamlined and brought into Elixir. Some other areas were left out, such as a complete structure for setting up CLI hooks, and most notably, hot code upgrades.
The documentation explains some of the complexities behind hot code upgrades: https://hexdocs.pm/mix/Mix.Tasks.Release.html#module-hot-cod... - so I won't go over those. More importantly, during discussions with different teams and library authors, it was clear there is not a golden path for hot code upgrades. There are different approaches, with different gains and pitfalls, and it would be too premature to choose one as blessed.
So what is the path forward?
While Elixir releases don't do hot code upgrades by default, its whole structure supports it. We use the proper names and structure everywhere. But the front-end to start the upgrade is not there.
I have discussed with Paul the possibility of Distillery building on top of what Elixir releases provide with a more complete feature set. It doesn't have to be done by Distillery either. Maybe someone (or multiple people) will provide a smaller package that focuses on hot code upgrades. Then once we gather enough collective knowledge, we can choose if and how to proceed.
Still, I believe Elixir releases cover the majority of use cases out there. But we will know for sure over the next months. At least, by making releases part of core, we hope we are easing the learning curve for releases altogether, which we will naturally lead more people to explore hot code upgrades.
Similarly I suspect many users of elixir are doing the "boring" thing that we're doing – load balancer -> stateless web server(s) running phx -> DB server. And they'd probably be more than happy with this.
This means that Distillery will remain the best choice for many of us, including myself.
I have a very simple deploy process with Edeliver and Gitlab CI/CD that basically just consists of pushing or merging a commit to master that passes the tests. There's zero down-time and it doesn't require containers on the server.
I'd also say that the "deployment story" wasn't any worse than Rails or other stacks in the past except in that people have a choice of having a stateful server.
This is how I'm doing it now: https://youtu.be/-mm44ADU3kc?t=172
That would be nice if it were true. However, for better or worse, Distillery was Not Invented Here, so what Elixir shipped with today is a reimplementation of the simple half of Distillery.
Although we should probably embrace this aspect of the ecosystem, (like with clustering of servers) I bet it's even easier than I can imagine.
I especially recommend this approach because it gives you more or less the most complete cross-section of some basic data structures, some web stuff, and some database stuff via their db adapter of choice, Ecto, which is excellent.
Each part can be a pretty steep (but fairly quick) adjustment depending on your background.
Appreciate the input, thank you!
[1]: https://pragprog.com/book/elixir16/programming-elixir-1-6
[2]: https://codestool.coding-gnome.com/courses/elixir-for-progra...
For understanding genservers there's The Little Elixir and OTP Guidebook, as well.
In my case I experimented with it for at least a year before I decided to jump in, my "experiments" are here: https://github.com/pmarreck/elixir-snippets (I tend to use single-file dev for exploring simple ideas; this is not a general pattern used in the community or anything, it's just something I discovered I could get by doing)
Thank you!
Or Poe's law, thus named in 2005, but based on a phenomenon that was well known by the time of Godwin's law.
Also, releases can be very small, after all the superfluous parts/symbols are stripped. Combine that with a fresh alpine image and you have quick-to-deploy containers.
I also recommend you to try to play with Plug. Things make much more sense in Phoenix after you understand how Plug works.
Stories to the contrary and hints how to make it work are welcome.
Knowing that, it's perhaps more pertinent to talk about what you can actually bring to that VM. Alpaca [0] could potentially be the solution. Interoperation with the rest of the VM languages and static typing on top of that. I have no doubt that most of the community would simply not use it, because they don't care about static typing, but I have zero reservations about saying it would be a better way to write code for the BEAM in the future.
Without something like that, it's unlikely that I will actually put any code that does more than something like routing messages to other services or the like on the BEAM in the future. Beyond a fairly low number of lines I simply don't trust anyone to write code that does exactly what they think it will do and no more in a dynamically typed language.
Not disagreeing with you per se but that was true 10 or 20 years ago. Nowadays the BEAM is very solid general-purpose runtime with the best parallelism on the planet attached.
> Knowing that, it's perhaps more pertinent to talk about what you can actually bring to that VM. Alpaca [0] could potentially be the solution.
Realistically, I'd say that ship has sailed. Elixir has a lot of inertia for that to happen at this point. People focus really hard on improvements over Dialyzer (like Dialyxir + improving warning messages), linting, standard formatting, higher-level code generators and what-have-you.
At this point I'd think an OCaml-to-Elixir transpiler that enforces the compile-time guarantees of OCaml and translates them to Elixir code is the much likelier road to strong static typing on the BEAM. I might be wrong though, it's just a speculation.
Which is a bunch of FUD (see top comment by EvanYou).
Elixir doesn't hold the same position for its abstract machine. BEAM, the abstract machine, has its development intertwined with that of Erlang's language development, but not-so-much with Elixir's. Elixir can stabilize while BEAM+Erlang continue to evolve "underneath" it. Given that Erlang syntax is pretty much† always valid Elixir syntax, Elixir doesn't even need to change to expose new Erlang stuff!
† There were big changes when maps were introduced, but another change of that scale seems unlikely at this point. (Unless Erlang gets a mutable byte-array type or something.)
In the end, I agree that the idea of a language which could be stable for a decade or more sounds fantastic! Given macros could add almost any other required feature should a good idea arise. Plus the BEAM and elixir compiler can continue improving performance and tooling.
In Rails the answer is always “use this popular gem” and in Go the answer is always “roll your own”. Phoenix is somewhere in the middle.
That interchange library can then be integrated into the Elixir stdlib—that's what happened with DateTimes in Elixir—but there's no reason it needs to be. It works just as well to just keep the interchange-type library as its own tiny little dependency that all these libraries pull in.
Plug happens to be somewhere in between the two outcomes you've presented, of course; its development happens very closely to that of Elixir (and it's arguably a sub-project thereof), but it ain't a core part of the language itself (last I checked; maybe this has changed in recent years).
It's always a little frustrating that folks pass on Erlang or Elixir as entirely dynamic. It's a lot closer to what you find in gradually typed languages if you take advantage of the tools available.
[0]: I know there are a few of these elixir projects, though I am not familiar with the parent's. Perhaps it's a typo?
In the end you need to some kind of compiler that does proper, rigorous and unapologetic type checking. If Alpaca could reach critical mass I could convince our project leader to use it, which would solve the problem entirely.
Sure, the ecosystem would likely remain the same in that the majority of people simply don't actually have any familiarity with strong, static typing, but at the very least you can now set up these guards yourself and you can have code that is much more likely to be safe and good.
If he isn't the one writing it, he's certainly been highly involved with the conversation.
Here's a stupid example I whipped up:
https://gist.github.com/amorphid/3dec7028b05bd10f6ff3180d199...
^^^ you wouldn't ever code it that way, but you should (in my opinion) know what each line of code is trying to do.
Even if you disregard the modeling power that you can get from this there is an upper bound on any elixir project after which any work on it becomes less and less easy to do, as with all languages that lack a static type system. We write assertive elixir code as much as we can, but that doesn't mean that you can actually guarantee anything about a code path that is less traveled.
dialyzer is also not the answer. Oftentimes we'll find ourselves in a situation where dialyzer complains because someone who made a major library simply doesn't use dialyzer. You might wonder why they don't in that case, because it would pick this particular issue up very easily, but the next time you run into some garbage error that dialyzer spat out that you are instantly reminded that you can neither trust it to be correct or safe, so people turn it off.
Elixir has upsides: I think it's solidly the best language for creating servers of different kinds, but it's near useless after you pass a fairly short distance with it. I would absolutely never, in my personal endeavors (as opposed to my working contract) write an elixir server that actually tries to do anything meaningful itself other than just route messages to other servers.
Whether or not this is based on nominal or structural typing is less important, because the feature itself only depends on differentiating between what is essentially different cases and having that be done safely and properly at compile time.
Elixir has no satisfying solution to this problem and it likely will never be able to. It is a huge penalty in domain modeling, which is why I personally don't want to use it for anything that actually has to deal with the flow of things.
To some extent I expected and wanted the VM to feel like it makes up for this by being so great that what it does, but there's no band-aid you can use to fix lack of modeling power.
You should really read that post I linked, and do a bit more research on this topic. The consensus in the Elixir community (where many members have written an order of magnitude more Elixir than you) is that static typing would be "nice to have", but is not critical by any means. Your post seems to be an attempt to spread FUD.
If you asked a community of people who primarily have never used languages that don't have garbage collection, they'll likely not understand at all why it could be a bad thing.
Someone who has used a language without garbage collection and is comfortable with basic allocation strategies is very likely to remark that having control of memory allocation and deallocation is very often something that you end up wanting instead of having to re-architect your solution in indirect ways to influence the garbage collector. This comes from having a wider perspective and the elixir community at large does not have this.
Given that the elixir community is also very cultlike it's hardly a productive thing to take what they say as the objective truth.
(I'm not saying this is an outsider at all, I've been a part of the elixir community since 2016. It's not really despite that I am saying these things, it's because of that.)
I advise some humility, as well as some effort to recognize that your opinions sound very dogmatic. You are of course free to like or dislike any language you want, but it is important to understand that going from one's own preferences to grandiose claims about a language's merits and usefulness is a rather big leap.
This is rich, coming from someone who claimed that anyone who wants a type system in elixir hasn't actually used elixir.
> You are of course free to like or dislike any language you want, ...
This is the most common reaction within the elixir community, to assume that anyone who doesn't put the language on a pedestal dislikes it or hates it. You might want to consider what that actually says about the community and the atmosphere of near unconditional admiration it displays. It's an extremely distasteful attribute for a community to have.
Elixir is useful for shuffling data from point A to point B... That usefulness has its limits, that's it.
You may want to read my original post more carefully. I said most people who complain about the lack of static typing haven't actually used it. Obviously there are some, like you, who have experience with the language and dislike the lack of static typing. And that is fine.
I mean, you only need to casually browse HN threads about new languages to find posts along the lines of "this looks interesting, but the lack of static typing means I probably won't use it." Instead of taking a holistic view of the language and its merits and researching its design choices and trade-offs, they adopt a dogmatic mindset and dismiss it outright.
>>This is the most common reaction within the elixir community, to assume that anyone who doesn't put the language on a pedestal dislikes it or hates it.
No, that's just me, and it was a figure of speech. Elixir community is very mature and welcoming, and most people in it come from other languages, and are able to objectively discuss the language, its merits and shortcomings. The post I linked in my OP is a great example.
>>Elixir is useful for shuffling data from point A to point B... That usefulness has its limits, that's it.
I mean, again, that's just your opinion, and it's a simplistic one. I obviously don't know the depth and breadth of your experience (and won't make assumptions about it, like you did about mine), but Elixir is useful for, and excels at, more things than simply "shuffling data". That is why there is so much excitement for it: the use cases it enables, and the elegance and simplicity with which it enables them, are nearly unparalleled. The lack of static typing does not change this fact, even if it can make certain things a bit more cumbersome towards the extreme end of the project size and complexity spectrum.
Not sure you and I read the same forum. My reaction to Elixir criticism usually is -- "well, if you dislike it for this or that reason, or simply don't find it useful, then don't use it, there are a lot of other languages out there". Many others do the same.
I haven't noticed cargo-culting on ElixirForum in a long time. You might simply be too cynical to differentiate people whose work life has been genuinely improved (and they express that sentiment on the forum) and people who overhype something you dislike (for valid reasons for your use-cases).
You CAN do hot upgrades, but many times the complexity of doing so far outweighs the benefits. For any non trivial app it makes updates/deploys to the app non trivial as well.
If you're using distribution, there's a good chance you need to deal with messages from both updated and not updated nodes anyway, so handling different shapes from different versions is a skill you already need to have; it's just hot load exposes it at more layers than previously.
Since working in Erlang for several years, when I have to work in languages where hot loading is not easy or common, it's always frustrating. Throwing away connection attached state to update code is a hard choice that doesn't need to be.
I have no experience with Erlang/Elixir myself, so please excuse me if this is a silly question – but, why can't the language/platform actually detect the difference in struct shape, and refuse to deploy the new version in that case? Or, demand that you provide some sort of conversion function that maps the old struct to the new one? Is this a consequence of lack of static typing?
The language standard library gives you hooks to relup the internal state of all of your processes: https://hexdocs.pm/elixir/GenServer.html#c:code_change/3
What it doesn't do is give you a hook to relup the messages that get passed between processes. If you do it right, you can pattern match against different versions of messages, and do the correct conversion function, but there are liable to be many, many, shapes of messages (including ones that you might not write yourself, e.g. coming from a library) and it might be difficult to catch them all.
To do it right, I would want to set up a lot of testing to make sure you can hot code reload safely. There is no specific guideline, and it probably hurts forward progress in developing guidelines, that generally, it's okay to have some downtime in any individual server node as erlang/elixir encourages you to think about failover anyways, and most elixir apps are relatively stateless webservers, so you've probably got robust load balancing and migration scheme in place in your cluster to begin with, making blue/green or rolling updates a "pretty much good enough" thing.
> Is this a consequence of lack of static typing
No.
Digging deeper the line from folks using erlang/elixir "in anger" was always that it was a supported feature but the reality is that most people shouldn't do it and wouldn't need to for the hassle it has.
That "separate mechanism" could be as simple as a plug that tells Mix to recompile and reload. Example (designed for - and part of - the Sugar framework, but should theoretically work for any Plug-based app, including Phoenix-based ones): https://github.com/sugar-framework/plugs/blob/master/lib/sug...
Hot upgrades like this are not foolproof (which is likely the reason why the above example is gated to :dev environments); there are other concerns like database migrations and other internal and external variances that make this inappropriate for most production situations. That said, these same concerns often exist for other high-availability situations as well, so if you know that you want zero-downtime code upgrades, figuring out a way to do it cleanly within the application is likely valuable as a way to avoid the hell on Earth that is trying to do this with, say, a bunch of load-balanced Docker comtainers.
So the better option would be to figure out a way to compile a new release, get the modules in place where the currently-running release expects them, then write up a plug similar to the above to check for new module versions and load them (or just do it as part of whatever mechanism you'd use to get the updated modules into the running release in the first place).
Just the one being newly packaged into Elixir 1.9+ by default does not yet support hot-loading...
With a single code base capable of having millions of processes running as the norm, some handling direct client requests and others handling in-progress work, data storage, holding open connections for transfers, etc...you get the capability to deploy without disrupting ANY of that.
Most run times can't do anything close to that. Think about all of those X million websocket benchmarks...now think about being able to deploy without forcing all X million to try to reconnect at the same time.
And it can do this while all of the nodes are connected and communicating with each other as well as the outside world.
For standard issue client server, it's not that big of a deal. You just separate the web parts behind a load balancer.
For background workers, long lived connections, web sockets, video/audio streams, file transfers (CDN)...it's huge.
- to understand exactly what your app is doing
- to understand exactly what Erlang/Elixir releases are doin
- to understand exactly how cod upgrade works
- to understand exactly or very damn well how to make the system handle those 1 million connections
- to understand exactly how to handle all the things you wrote about
And then, and only then will you be able to "think about being able to deploy without forcing all X million to try to reconnect at the same time."
There are no magic bullets.
It’s pretty magical. There’s a reason people love it.
Certainly, don’t use it if you don’t need it...it introduces extra complexity...but if you do it’s really hard to beat.
What if something goes wrong during the rollout? For example, if you change the database schema or upgraded your database engine or changed your back end authentication approach, it can break the old code. Then how will you know whether it's a problem with the old code or new code if many nodes are running both?
That said, even with Distillery, if something goes awry, you'll need to know the warts behind the magic :)
Its not like you don't need graceful reconnect handling logic for the websockets anyways -- net-splits and endpoint failures are a thing.
If I'm running 1M sessions per machine, and each machine can handle 10k new connections / second, rolling restart is really expensive. If I'm running 10k sessions per machine, and can handle 10k new connections / second, rolling restart isn't too bad. This generalizes to really anything that gather essentially ephemeral state, but that state is costly to gather (tcp flows in this case, data caches in others, etc).
BEAM excels at applications with huge numbers of sessions per machine, which is why some users really value hot reloading.
Editted to add -- the OTP application update sequence doesn't necessarily need to be used. Where I work, we certainly don't do that. Just a little bit of logic around code:soft_purge/1 and code:load_file/1
BTW: we were the first who did 1M long-living connections load test with Cowboy on a single EC2 instance back in 2011. And 3M long-polling HTTP requests on a single beefy physical server.
It was before WhatsApp upstreamed their optimizations and before Phoenix team made it easy.
I don't want to give the impression that this is happening on a single server, either! I did the cowardly thing and threw more boxes at the problem. :)