Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

We're building a startup ( https://www.batteriesincl.com/ ) with Elixir and Phoenix 1.7rc (git master really). It's been amazing; I could not be happier.

- We went hard on components and it's made building UI's easy. In fact I wrote a test library to make component testing easier.

- Live view is so easy with a good component library. I'm not a designer, but with snappy interactions and easy to use components, it's not hard to get something that's exciting.

- Elixir is a very nice language to write distributed systems in. Functional in all the right places plus it has OTP.

- The community is full of very senior people who freely answer your noob questions.

- On-boarding people has gone well. The syntax is friendly enough that experienced engineers grasp the basics, leaving functional programming and OTP left to discover.



> The community is full of very senior people who freely answer your noob questions.

The role of José Valim, the creator of Elixir, can't be understated. He's everywhere, he's got his hands in many of the top used libraries, and he's incredibly welcoming and responsive to all the inane GitHub issues I've opened over the years.

People wrongly compare Elixir to Ruby, but I wonder if he decided to recreate the welcoming and newbie friendly community and leadership of Ruby.

(Chris McCord, the creator of Phoenix, seems like a pretty swell guy too)

Also, a note on the upstream code: Elixir and its core libraries are some of the few projects you get a quick answer to your issues and they aren't closed because they've gone stale. 18 open issues, 5k closed on the core repo is an impressive ratio these days.


To highlight this point, Jose’s in this thread and across HR. He’s an inspirational leader.


What’s been your experience with Live View?

I ask because I’m under the impression that using it comes with some sizable scaling problems, which somewhat defeats the point of using Erlang in the first place.

I could be wrong though. Would like to learn more.


Each LiveView requires a persistent WebSocket connection to the server. This means it does have a different scaling profile than the usual request/response lifecycle, but the Erlang VM is greatly capable of holding millions of connections at the same time and therefore is a perfect fit for the LiveView model.

In fact, LiveView is built on top of the same Phoenix Channels we used to achieve 1 million connections on a single server. So I would say that LiveView fully leverages the Erlang VM strengths. :)


Thank you Jose for all you do, create and how you help developers around the world.


What situations would face possible issues with the persistent-socket-per-view approach? A sprawling app with hundreds of distinct views that over-use LiveView? Would there also be multiple persistent sockets for each particular page?

I can't imagine the type of site that would need that sort of structure. Typically you'd have your highly-interactive primary subset of your app, about <10-25% of the routes/views which gets 75-90% of your traffic. While the other 75%+ of routes are just simple static-y/REST CRUD/server-rendered pages.


We use LiveView extensively and one meaningful issue we have is users with intermittent connections on mobile.

On a "normal" req/resp page, their intermittent connection would manifest as a blank then slowly loading page: things that the user understands as being the result (in a way) of "bad internet".

With LiveView, the site simply becomes unresponsive. Nothing changes, and the user interprets this as the site being "broken" (which in a way it is)


Some of the 1.7 stuff has an alert banner that pops up when the connection is broken. I think that could really help.

However I haven't put that in our app as I have seen other issues of flakey connection reconnect issues, and I would hate to make any of those more visible with a flashing notice.

- https://github.com/phoenixframework/phoenix_live_view/issues...

- https://github.com/phoenixframework/phoenix/issues/5102

- https://github.com/phoenixframework/phoenix/pull/5024


> I would hate to make any of those more visible with a flashing notice.

idk I haven't used LiveView in production but an interface that offers a well designed fallback "refresh" button helps for unpredictable edge cases, more so than the negatives of added complexity, which yes implies failure but is also certainly better than failing to optimize for reality.

I ran into a similar hypothetical issues, that was really in practice an edge case, because we cached views for typical users. But I still provided a way to force reset of both server caches AND Localstorage caches because otherwise such an option was limited to advanced users/devs who either know backend or frontend.

This is win-win ultimately because the user feels taken care of even in niche failure edge cases and QA/devs doing testing aren't forced to use exceptional means to reset state.


there are not a ton -- the pain points mostly boil down to:

Don't use it for extremely latency sensitive UI's (there is around trip and most of the time for most uis this is not an issue even using a server across the world).

Don't use it for animation heavy UIs (you can do animations with it but think things like game UI).

It does not work offline (in most cases it s the same point with standard web and offline).

All of that said, the answer to your second "question" is: There is a process (this is an erlang process and VERY light -- much lighter than a thread its not unusual to have millions of them running at a time in beam) per active page. So one per use per tab or session.

Also yeah, there is no lock in -- you can have 99% of your pages use static rendering (dead pages) and just use liveview for the ones that would benefit. The choice does not come with sharp edges.


IIRC (don’t claim to be an expert) the main issue is gracefully handling the case where the server end goes away unexpectedly - network issues, reboots, that kind of thing.


Does that include deploys?


yes active clients reconnect automatically, if you are doing rolling traditional deploys its great. It is running on beam so you can do live in place deploys too (although it is much more work).


In a situation where you hit the scaling limitations of the BEAM, you’re going to have the budget to address it.


> I’m under the impression that using it comes with some sizable scaling problems

Interesting. I have the exact opposite impression (I'm not experienced in live view; only written 2 small utilities). Erlang (therefore elixir) is fundamentally distributed, so as you mentioned, it would defeat the purpose.

So why gives you that impression? LiveView is just a smart/reactive socket built on Phoenix that behaves just like any other elixir process right? Why would it specifically have scaling issues?


LiveView has been pretty great. Mostly it's just a couple of `handle_event` methods away and we have a fully reactive UI.

I haven't seen any scaling issues for LiveView. Under the hood there's a websocket that push and pull events from a running GenServer for each session. Since each process is independent it's horizontally scalable as long as your able to route websockets to the same process in a cluster.

While not the same, I do know that single machine has been able to scale to a couple of million connections ( https://www.phoenixframework.org/blog/the-road-to-2-million-... ) with phoenix channels. Which are harder to scale than independent live views. Also our use case is for smaller scale than that (Not too many companies have a million people looking at their ml deploy pipelines) so I haven't been too worried.


@chrismccord

If you’re reading this, would be super interesting to update this 7-year-old benchmark to use Live View (and the latest stack). Thanks for all you do btw.


Could even switch over to Bandit which was on a recent Thinking Elixir podcast

> In recent performance tests, Bandit's HTTP/1.x engine is up to 5x faster than Cowboy depending on the number of concurrent requests. When comparing HTTP/2 performance, Bandit is up to 2.3x faster than Cowboy

https://github.com/mtrudel/bandit


I don't think that 1.7 is going to really have bandit fully supported on launch: https://github.com/phoenixframework/phoenix/pull/5071

Once it is supported though, I will be on that for sure.


The PR you linked to adds support for generating new phx apps with the relevant change already incorporated; it’s just a generator change. We’re waiting a bit to incorporate this change to ‘soft launch’ Bandit support.


Bandit author here. We’re fully supported on phx 1.7+; it’s a one line change to your existing app, described on the Bandit readme!


That’s a great point and Phoenix 1.7 is the first release to support the usage of other web servers.

2-5x speed up would make Erlang surprise Java/Go/etc.

https://stressgrid.com/blog/webserver_benchmark/


Bandit is written in pure Elixir which is a bit undesirable to integrate with pure Erlang projects. From what I gleaned off the podcast, it's a subset of the HTTP features available in Cowboy, specifically those that are available on the Phoenix side, and cleaving off the unused functionality realized much of the performance benefit.


Bandit author here. Correct! The byline of Bandit is ‘a web server for Plug applications’ and being able to focus on that narrowed set of requirements is a large part of where the perf boost comes from (less code, easier to reason about, fewer processes, etc).


I'm also doing a solo-startup right now that relies heavily on LiveView and Channels/Websockets. I wouldn't have been able to build it by myself without Elixir/Pheonix. It's absolutely fantastic, can't recommend it highly enough.


How did you settle on Elixir/Phoenix? Did you consider any other languages + frameworks?


100% this. I started with it solo and I was able to achieve what would have been very hard for a three person team with the standard js app + api app + backend app.


What does your startup do? I'm not 100% sure based on the blurb, but keen to see product ideas where that tech stack shines :)


We haven't launched yet (Q1 Next year is the target), so the landing page isn't great yet.

We're a platform as a service that you can install on any cloud via Kubernetes, or on your own self hosted Kubernetes.

So distributed systems, automated remediations, machine learning ops, etc are the areas we're using elixir.


What are you using for the component library? I was thinking of trying out Elixir and if there’s something that works well with it I’d rather start there than by trial and error.


I would recommend Petal: https://petal.build/ It's what I used for quite a while and I think it's the most comprehensive currently.

We're building two different UI's that share a common design language so we ended up creating a common UI application in the umbrella project.


Do you happen to know if this was built with accessibility in mind? There's no mention of it on the website as far as I'm able to tell.

Looking from the outside in, it is also unclear what relation the Petal framework has with the PETAL stack, I am confused by the name collision.


Thank you.


> Elixir is a very nice language to write distributed systems in. Functional in all the right places plus it has OTP.

This is the biggest blocker for introducing Elixir to any company I work at. I don't want to become / hire experts in the OTP and the Erlang VM.

I'm ignorant about it in general, but my feeling is it's not only a new language, it's built on abstractions that I'm not sure I'm comfortable owning or operating. Is that wrong and how did you handle the tradeoffs for your company?


My answer to this is the following: do your software have by any chance, SQL writes and HTTP request to any third party APIs within the same endpoint? Or maybe it uses some background job processing that's not SQL based and you have at least one endpoint where a job is enqueued AND something is written with SQL?

If the answer is yes, you already have a distributed systems with all the downsides and none of the mitigations.

If that's the case, you can also use Elixir without still knowing anything and have the same level of penalties.

It's really hard nowdays to avoid distributed systems, it just happen that people don't realize they have one.

The classic Rails + Postgres + Sidekiq is a distributed system


It's not the distributed system bits. It's the heavy abstractions over the distributed system bits that make me question if operating it requires specialized knowledge. I have a performance issue or an edge case with actors from the OTP. How do I debug and resolve it? Say network partitions aren't behaving like I expect and there seems to be data inconsistency or lost. I think you're doing a disservice when you pretend this is the same class of issue as a three tier app you can Google and StackOverflow for.

This dependency should raise questions for anyone looking to adopt this, and I want to know how people have attempted to mitigate this vs. handwaving.


The central abstraction isn't IMHO that heavy. It's just this:

A process has an isolated execution state, a message queue, and an id.

Messages can be added to the queue using the process id (from local or remote processes), and messages can be pulled from the queue by the process (with pattern matching, first matching message is removed from the queue)

If you have a performance issue, introspect with erlang:process_info to see what your processes are doing, and language indepedent tools to see what the VM is doing. process_info gives tons of information, IMHO, much more operable than other systems I'm familiar with. Ex: if your process has a large message queue, either it's not processing it's messages fast enough, it's selectively processing messages and leaving junk in the queue, or some combination; you can get inspect the queue with process_info and maybe figure that out. Lots of possibilities for why too slow, maybe it's waiting, maybe it's slow numerically, etc; process_info can tell you what function is running, which is usually helpful, etc.

Network partitions and their effects are highly application dependant, but if you have multiple nodes in your existing system, you have to think about it already, or choose to ignore it, which you can also do here, although if you use mnesia, you shouldn't ignore it: the default is for partitioned and then rejoined nodes to stay apart, waiting for an intervention, which may be the only reasonable default (because there's no right answer), but it's not what people usually want.


One note, the message queue has a limit in size,going over the limit will drop messages.


If there's a limit, it's new and I can't find it in a quick search; I did find a 2014 PR that was rejected because dropping messages violates core semantics, IMHO killing the recipient would be OK though. Traditionally it's unbounded, but when you exhaust the memory of the host (or the ulimit, if set), you'll likely loose the whole VM.


Turns out it was specific to the application I worked on, sorry for the misinformation.

It is configurable: https://www.erlang.org/eeps/eep-0042


EEP 42 is not implemented in mainline OTP. If it were, the proposal would likely link to the implementation, and the EEP index in EEP 0 would show what version it was first included in: https://www.erlang.org/eeps/eep-0000

If your application is using something like this, you're running a modified beam (which is fine; my professional experience is on a modified beam where we added a process_info option to drop all messages in the queue of a given process, and to allow for messages to be added to the front of the queue, neither of which would be acceptable upstream, but both of which were super handy for us)


It's been a long time, so i could be wrong, but I remember we did face the problem at some point.

Either way, not having a limit makes it even easier to use, which I really appreciate


I see your point, I still disagree, I encountered some bugs that were incredibly surprising with postgres + sidekiq combo (made perfect sense though)

Elixir doesn't distribute anything unless you tell it to, keep that in mind. You can run it like a normal rails app. The OTP by default runs on one machine and as such, it's not subject to distributed systems law.

And you can still scale horizontally in the traditional way (deploy the app to multiple machines), no need to learn or use the multi-nodes.

The debugging experience is the same. Of course you have to learn the OTP library at some point, but it's equivalent to study the ruby standard library: it's natural, must be done, like for any programming language.

That being said, the tools provided are really nice, given erlang is very old, the tools do exist. And you can connect to a production machine with an "irb session", except that the one in elixir is in the same memory space as the main process, so you can actually inspect in memory stuff, very powerful and dangerous.


You can kinda ignore OTP outside of the initial "run these (erlang) processes when the vm boots", which is a few lines - often scaffolded out automatically.

But then when you suddenly need some async task x service x parallelism its there.

I guess if you're building a company it does behoove you to at least read a bit about it but you don't need every team member to be an OTP expert. If you can understand how an OS runs processes at the most basic level and how javascripts async/await works you're already most of the way in a practical day-to-day sense.


Ignore OTP for the most part. Just write good code that works at one level at a time. Then when you hit something that needs OTP I send them towards Designing Elixir Systems with OTP

https://pragprog.com/titles/jgotp/designing-elixir-systems-w...

This is all about the onboarding experience of engineers coming onto a team. So you have to be careful with the projects that engineers newer to elxir get. But that's part of the cost of a more niche language.


The abstractions you need to critically know are map and reduce. It's not functional like Haskell, the limit at which you need to "think about it being functional" is that a value inside a variable can't change from underneath you when you pass it to a function. It pretty quickly changes from "I have think about values not chamging" to "I don't have to worry about values changing"


The abstractions I’m talking about are the Erlang VMs and the primitives like actors that are provided by the OTP. I have cold sweats thinking about needing to debug a non-obvious performance issue and diving into that layer.


Ok. Well there are a lot of other options for performance and no matter what system you're in (python, ruby, rust, jvm, c++) those kind of performance debugging is going to be a slog, and GenServers are relatively easy to work with and the VM gives you a lot of tools to figure it out. Most people at scale seem to be doing okay with elixir.

I will say the one thing that I do see coming up over and over again is OOM errors, but I personally feel that's because there are a few gotchas that juniors don't always know about




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: