Dynamic Forms with LiveView Streams
fly.io
fly.io
LiveView is game changer, same user experience (even better) with 10x better developer experience. No much dual state management and things just work.
What makes this better than say a traditional ruby/rails or django app with maybe some htmx to save doing the JS side of things?
This video explains it better than I can: https://www.youtube.com/watch?v=JvBT4XBdoUE
> You get a concurrent request processing spread across all of your cpu/vcpu cores out of the box
That's neat, but this doesn't matter until you reach serious scale, as scaling a rails app horizontally by throwing more server instances works for a long time
> The fallback controller pattern for error handling is incredible for boilerplate error handling
Sounds just like controller inheritance in rails
> Worst case latency is very stable
So is rails, worst case latency is generally caused by slow SQL requests or having to render complex documents (which can be offloaded to the background easily)
> The query builder/data mapper, Ecto, is IMO far better than ActiveRecor being more explicit and prevents N+1s out of the box
I don't have a problem with ActiveRecord, and while N+1s are easy to create, there are a ton of tools to help prevent these in rails. Can be a hinderance for junior devs or devs without rails experience though
Once things get complex, you're gonna be writing SQL directly anyway
> Eex is built on compile time linked lists
Cool but sounds irrelevant for 99.9% of cases, string interpolation isn't what causes rails apps to be slow
1. Development is faster if your compiler (or code loader), tasks, and everything else is using all cores by default.
2. You get concurrent testing out-of-the-box that can multiplex on both CPU and IO resources (important given how frequently folks complain about slow suites).
3. The ability to leverage concurrency in production often means less operational complexity. For example, you say you can offload complex documents rendering to a background tool. In Elixir this isn't necessarily a concern because there are no worries about "blocking the main thread". If you compare Phoenix Channels with Action Cable, in Action Cable you must avoid blocking the channel, so incoming messages are pushed to background workers which then pick it up and broadcast. This adds indirection and operational complexity. In Phoenix you just do the work from the channel. Even if you have a small app, it is fewer pieces and less to keep in your head.
At the end of the day, you may still think the bullet points from the previous reply are not sufficient, but I think they are worth digging a bit deeper (although I'm obviously biased). :)
2. Our test suite is pretty good! It's limited by the longest test runs (basically selenium tests that are slow because browser interactions are slow), circleCI allows pretty easy parallel testing
3. I think the same reason we offload long-running rails processes still applies . The only thing a long running rails request blocks is further requests to that particular thread handling the long request. Usually some other request will end up getting queued to that thread which is the issue. So it's a load balancing issue, as well as a UX issue (you don't want an HTTP request to take 3 mins loading a long report). Unless Elixir can indefinitely spin up new threads, this is still a load balancing issue, determining which server incoming requests are routed to
We don't use action cable so I can't comment there
3. Exactly. This is not a problem in Elixir. If it takes 3 minutes to render a request, all other incoming requests will progress and multiplex accordingly across multiple CPUs and IO threads. This also has a direct impact on the latency point brought up earlier.
> That's neat, but this doesn't matter until you reach serious scale, as scaling a rails app horizontally by throwing more server instances works for a long time
You can do that, but its cheaper to get more out of each cpu and Elixir/BEAM give you that for free with a similarly flexible dynamic language.
> Sounds just like controller inheritance in rails
Not exactly, it works on the basis of pattern matching and the Fallback functions are included into the plug (think Rack) pipeline. This makes it faster and you don't have the problems of inherited methods stepping on each other. You also get to match on really specific shapes and cases to handle really granular errors without much effort or cognitive overhead, and you don't need to do things like catching errors like people often do in Rails controller error handling with rescue_from.
> So is rails, worst case latency is generally caused by slow SQL requests or having to render complex documents (which can be offloaded to the background easily)
You elixir application will often be doing things like background work and managing a key value store. You can do all of this and saturate the cpu without latency exploding. The scheduler in the BEAM will de-schedule long running processes and put them in the back of the run queue. Again, you get this for free.
> I don't have a problem with ActiveRecord, and while N+1s are easy to create, there are a ton of tools to help prevent these in rails. Can be a hinderance for junior devs or devs without rails experience though
That's all well and good but it's a nice feature in Ecto. Ecto also hews closer to SQL, and you can compose reusable pieces of queries in a way that is far more manageable than anything ActiveRecord scopes offer. We (where I work) write anything short of complex CTEs in Ecto's DSL, a lot of stuff I'd never try to do with ActiveRecord. It's just a lot closer to SQL and gets some nice compile time assurances.
> Cool but sounds irrelevant for 99.9% of cases, string interpolation isn't what causes rails apps to be slow
Rendering collections of nested partials in Rails has always been slow and eats memory. This isn't an issue with EEX. They also render faster locally.
I'm always amazed by how malleable the BEAM and the patterns built on it can be. I don't think anyone predicted Erlang to (IMO) reign supreme in frontend development. I say this having written tens of thousands of lines of React and Svelte in production, on top of all the hobby projects over the years.
Off the top of my head there's not many languages that can easily handle a million processes on consumer hardware, while the developer only has to think in single threaded mode because deadlocks and data races are literally impossible.
If you're building a server of any kind, the BEAM is the bee's knees. You can always resort to using a sidecar process or a Rust NIF for the high performance hot path.
This is a big component of the secret sauce. Writing top down, happy path code as if you're just exploring an idea and then decide to scale it to distributed nodes without changing the implementation is just absurdly practical and blows every other VM out of the park.
Does Elixir somehow automatically solve the case where a row in a database is loaded into memory simultaneously across multiple requests and a value is incremented? (can be easily solved with row locking, but still needs to be done explicitly at the application level)
If you need a global, cluster wide counter, spawn a process with name {:global, :whatever} and let it be the source of truth for this value.
Depending on the problem, there are multiple approaches you can take. And a single Postgres instance is able to deal with massive concurrency, there might not be any need for premature optimization when SQL transaction could do.
One solution is to dynamically update the value with Ecto on_conflict if possible.
Something like this, where you try to insert instead of update and have a constraint on the id for example:
> on_conflict: [set: [great_column: dynamic([t], fragment("? + ?", t.great_column, 1))]]
https://hexdocs.pm/ecto/constraints-and-upserts.html#constra...
[0] https://gist.github.com/Gazler/b4e92e9ab7527c7e326f19856f8a9...
The developer experience is great and Phoenix is just a great framework. The speed in which one can create stuff is mind blowing which is going to be the biggest pull factor into the language imo. My main issue is that Elixir is great for a lot of hard problems but for the easy, common problems it is lacking a lot in libraries and so on. It also has a high learning curve.
Although it is probably just going to get better in time the learning curve will probably not.
That said, with every new language comes idioms, tools and libraries that one has to learn, but this is true for all languages.
I think the hardest part is to let go of some of the old practices and embrace functional programming as much as you can.
I would like to work with elixir professionally but have yet to find an employer that uses it in production :)
And even if they do exist, the documentation is often very sparse or the library is not super actively maintained like in the big popular languages.
These are all stuff that obviously only get better with time and when people start working with elixir. It's not something that makes Elixir bad, it is just that you have to take that into account that when you want to do something very specific with an image or want that api wrapper for that api that isn't well known then you 'll have to implement it yourself instead of relying on some third party package.
The best thing for someone like me (back-end dev) is no need to write JavaScript, care about zillions of build systems, having the Node/npm drama yet I can create nice and "progressive" web apps. In fact one of the projects I'm building is a powerful online forms builder.
Easiest way to get component usage examples ATM is to generate a phx project, and run a mix phx.gen.auth and phx.gen.live command.
But more ontopic, has anyone here already used these LiveView Streams in production? Based on this example it feels like I'm having to manage a lot more myself with the only real advantage being less memory usage, right?
I'm just wondering where/when it makes sense to play with this instead of, for example, temporary_assigns with phx-update="append". Are there more advantages than the memory usage reduction?
It seems to look very similar but I didn't see whether LiveView has the "sticky" websocket server requirement like Blazor. With Blazor all of the user's state is in memory on one specific app server. And if a connection is lost the app is completely unresponsive.
It's a great dev experience but I've seen comments about scaling and interaction latency concerns. Seems like it'd be great for intranet corp apps.
Is anyone running either tech as a public facing service?
To elaborate a bit, while each LiveView client is indeed associated with a long lived process in the LiveView server, the Phoenix framework provides hooks and other lifecycle management tools to minimize the impact of disconnects from eg blue green server deploys. Most interestingly for example with forms, the "automatic form recovery" does a process where when the reconnect happens it delays re-rendering until the client side has had a chance to send the current form state back to the server and allow it to synchronize.
I wonder if the framework is particularly tied to Elixir or if it could be done in a more popular language so more people will take it up.
edit: and yes I know WhatsApp was/is written in Erlang, just to save you commenting.
You need to learn Elixir. Learning Erlang isn't going to be particularly helpful.
> I wonder if the framework is particularly tied to Elixir or if it could be done in a more popular language so more people will take it up.
Blazor (C#) and StimulusReflex (Ruby) are the ones that immediately pop into my head.
That said, learning Elixir first will at least get you in the right mindset, and then Erlang becomes an easy lesson in syntax.
All programming languages are Turing-complete thus compatible. So you shan't need much more than Brainfuck.
But if you consider all the benefits the language and the platform it sits on (the BEAM) provides, no, you cannot do Live View as easily and comfortably than you can do on Elixir. Because its model is strongly tied to lightweight processes, message passing, mailboxes and pattern matching.
You can implement Live View in assembly or Python or Rust, but it's not going to be very quite as ergonomic and productive.
Also, you don't need to know Erlang to be productive in Elixir, but knowing the Erlang ecosystem (i.e. OTP) is going to pay dividends, since it's very powerful and at your fingertips from the Ruby-like comfort of Elixir.
XSL-T 1.0 is Turing complete but you can't really write a C compiler in it - despite what some people may have been heard to claim at various XML conferences in 2000 - because what matters in most things is what rights and accesses the language offers you and XSL-T 1.0 does not give you the possibility of loading in non-XML files.
I would love to see this.
As others have pointed out there already are lots (missing at present are LiveWire and there is even a LiveView.js that uses the same JS that Phoenix LiveView uses... it's a very unfortunate name).
Having said that, most of these implementations aren't "live" in the same way you get with the Erlang virtual machine where each user has a dedicated process. It's at least possible in Go, but most of these other implementations resort to AJAX and the like.
My understanding of the origin of Phoenix is that Chris McCord tried to create something like LiveView in Ruby, but the concurrency system didn't support the vision he had for how it would work. He cast around a bit, discovered Elixir and create Phoenix and then LiveView.
The concurrency story in other languages is improving over time, but they're still trying to catch up with what Erlang was doing in the 80s. Building complicated applications on top of the BEAM and OTP is a joy compared to any other system that I've used. You learn a few simple approaches, and many things you've had to think about just go completely away.
An example of the kind of problems is at [1]. It's not hard to find other such discussions.
The other thing I wonder about is cross-platform. If you're using something like React or Flutter there are various solutions. But I haven't run across anything substantial that's oriented towards Phoenix LiveView.
[1] https://elixirforum.com/t/live-view-fallback-with-no-websock....
The latency issue is a real one. Long polling can also help there, but realistically if a substantial portion of your user base is on poor connections you’re going to be much better off with a traditional server rendered web application than something like LiveView or a SPA.
I’m not sure what you mean about cross-platform. LiveView is for building web apps, so there’s only really one platform to speak of: the browser.
> I’m not sure what you mean about cross-platform. LiveView is for building web apps, so there’s only really one platform to speak of: the browser.
Similarly, React is for web apps but React Native has emerged such that your knowledge and some code can carry over, and now there's even React Native for Web so that you can write React Native and run it on the web in a way that at least tries to maximize code portability. It sounds like there hasn't been an emergence of equivalent tools with LiveView... but that's fine. As you say, LiveView is for the web and if that's the one thing you're targeting, especially for an internal app so internet connectivity is reliable, it's fantastic and possibly the best solution that exists now. There's certainly nothing wrong with making a great tool to full a particular (major) need!!
Soon™! https://native.live/ and https://github.com/liveview-native/live_view_native
I am Ruby/Rails developer myself and StimulusReflex seem to be the equivalent but idk, is my feeling true?
My dream stack that I am using for my business is Phoenix, Live View and Stimulus.js with Tailwind.
I have built projects on Laravel, Django, RoR, React and a long tail of in house frameworks, and nothing feels as good as this stack for web apps. Ruby on Rails is quite underrated, but the BEAM is a spaceship that fits neatly in the list of unknown unknowns for 99% of software engineers: they do not know what the BEAM can do for them. Its process and mailbox model feels futuristic in its expressiveness and simplicity.
It's easier than you think to get used to Elixir's functional language model. For me it was super easy, while with stuff like Haskell or Prolog I felt like I had to rewrite my brain from scratch.
To end my proselitism, I am adamant that the first Live View demo by McCord and Valim will have the same staying power if not popularity as DHH's first demo of Ruby on Rails which spawned the modern MVC framework architecture of the past 15 years.
The Live View model is in my eyes the next generation of interactive web architecture, the true successor to MVC.
I am curious, why both LiveView and Stimulus.js? Why and how are you combining the two together?
Imagine a disappearing burger menu, or a tooltip widget, or a "click here to copy to clipboard" link. For those sprinkles of interactivity, you need JS and Stimulus is ideal for that use case.
I just have my Live View process render
<input type="text" data-controller="copy-on-click" value="..." />
which is easy to understand and plays great with Live View's model.Don't forget that JS was born for this goal: small snippets of interactive behaviour; it was never meant to be the entire system.
Either way, I'm shipping it to production immediately!
It seems to me a better investment of my time to learn a solution that's just as lightweight, but I can use everywhere (i.e. in other projects, or in static non-Liveview pages)
Client-side logic makes sense in a vacuum, but then your crappy React app is in fact a distributed system, with all the complexity that entails. The radical idea of Liveview and similar efforts is that it's better just to return to basics and keep the logic on the server. It would be lunacy to try and build a distributed system with Javascript, yet that's the direction the SPA world is/was taking us.
I do not like how it's trying to fit logic in custom HTML attributes. Doesn't look great to me, and would pollute my Liveview templates with a matryoshka doll of nested logic: There's a top-level Elixir module, which contains a HEEX string that creates HTML, which itself contains Alpine.js logic as an attribute.
Feels cleaner to me to just create a new .js file that exports a controller, and just instantiate it from LiveView (which still pollutes the HTML, but much less than Alpine does)
That said, there is no good or bad answer. Alpine or Stimulus are certainly better than instantiating Vue or React. HTMX could fit very nicely as well. Use what you're comfortable with.
Rails, it was this one 17 years ago: https://www.youtube.com/watch?v=Gzj723LkRJY
Live View, I'm not sure which is the first demo. There's "Build a real-time Twitter clone in 15 minutes with LiveView and Phoenix 1.5" (https://www.youtube.com/watch?v=MZvmYaFkNJI)
I've been working on a personal project which is very list heavy. I started it in pure LiveView using streams but ended up switching to a React SPA hosted in a non-updating LiveView div. Along with missing the reordering (which is needed a lot in the app) the amount of state I needed to hold on the server to wouldn't scale.
htmx is a frontend library first and foremost, that dictates some patterns you may want to implement on the backend. LiveView is a backend-first framework, with excellent support for interactive frontend stuff.
In Rails this would have been a "partial" with some underscored filename