Phoenix 1.7 for Elixir: Edit a Form in a Modal
blog.appsignal.com
blog.appsignal.com
LiveView is great, but it's sort of becoming the default way to render everything--even static pages! TFA is a good example of this, where they use a Live view to render...a static list of icons.
The problem with this is twofold:
- LiveView is not free. It seriously affects people with unstable connections, for example. It creates a new class of silent bug at the client/server interface, where the client just sees "broken" pages (instead of 500 pages).
- It adds cognitive overhead. We bloody know how to do server side rendering. We've been doing it for 30 years (!). It's a good model. Don't make me learn something else, please.
Phoenix, which is a jewel of a framework, is risking becoming an "SPA-like" framework, with a new paradigm, and a whole lot of overhead to learn, footguns you've never encountered before, bizarre failure modes, etc.
I mean, TFA even has a section where they talk abut an obscure but that "...is a doozy, so I will save you some hours of debugging."
We don't need this!
One of the things that made Phonix/Elixir great is how simple and opinionated the core team has been. Conventions are Very Strong, but not magic. Unfortunately, LiveView is so cool (it really is!) that no conventions were built around it and it's now used for everything.
IMHO LiveView should be used with a very clear heuristic:
- If the page is static, render a static page.
- If the page is dynamic, and the content changes depending on calculations happening server side, use LiveView. It truly shines here.
- If the page is dynamic, and the content changes depending on actions performed by the client, use JS.
I'm fairly involved in Elixir, so I run a risk of catching some frustration for agreeing with you here.
I get why they are going the direction they are, but I think it comes at increased complexity for beginners (that don't want to use LiveView). For example, Phoenix components (which are usable in static server-rendered controllers) live in phoenix_live_view package.
All of that said, Elixir / Phoenix is the best development experience I've ever had and I have no plans to stop using it.
Sort of like OP, GP, etc.
Also, I agree with your heuristics. It is untenable to make a round trip to the server for things that can be done purely on the client side. Alpine.js is great for managing purely client-side stuff IMO (as are LV hooks, although I haven't used them too often), but they don't really mix well, and Alpine seems to have fallen out of favor lately with the Phoenix crowd as the complexity of people's projects starts to reveal the cracks in the paradigm.
To add to your heuristics, the `phx-loading` attribute should be used when possible to notify the user that 1) yes, you made the action, and 2) we're still waiting for a response.
By the way, did something like activeadmin pop up?
I also think they kind of jump the shark with the core_components.ex tied to Tailwind.
I've had a brief look into the ecosystem, but I found it quite hard for me to understand how a multinode Erlang cluster is setup.
[0] - https://www.manning.com/books/elixir-in-action-third-edition
Yes. I'm a .NET guy by trade and I've poked around Elixir/Phoenix a bit. I wish this stack (and its concepts) would gain some popularity in the enterprise so we could get rid of microservices and SPAs entirely. 99% of companies don't need Microservices, they solve problems they don't have at costs they shouldn't have to pay (velocity, complexity, etc.) SPAs could go away for most LOB apps with Phoenix Live, Blazor (.NET), or even just classic server-side web apps + Htmx.
Sure, there's some consistency in the tech stack, but I do not need a full-blown TypeScript/React SPA any time I just need a page with a form.
Ever looked up Orleans? https://learn.microsoft.com/en-us/dotnet/orleans/
Actor model for .NET
There's also AKKA.NET
If you're ever looking at microservices as possibly a good idea you should definitely consider "can I just do this in erlang." Usually the answer is "yes, but no, because of company politics" and those hurt a lot once you know what you're giving up.
I think it's bad design to have your runtime handling those kind of problems, a generic scheduler should be, not the runtime of your language.
I feel like protobufs are an improvement to Erlang serialization for talking to possibly older versions of things.
I think of Elixir as a way to get full control of the stack in one runtime. It has process management and clustering built right in, which is kind of redundant with everything k8s offers. It gives you so much control over so many things with one runtime and language.
Contrast that to a runtime like Ruby/Rails where it’s “run a server process and don’t attempt anything outside of that” — k8s can actually be helpful for process management, clustering, and service discovery because doing so in Ruby would be a huge effort that requires cobbling the Ruby, Linux, and k8s things together that you just don’t have to do in Elixir.
- A phoenix application basically can be seen as a "microlith": monolithic codebase (even with umbrella setups!) and during runtime there a lots of independent processes that scale over clusters of VMs, where partial failure doesn't neccessarily affect the rest. So no truly independent deployments of microservices, but most advantages of the pattern, without HTTP-API overheads or similar, since sending messages between processes is a natural thing to do anyways.
- Liveview extends that by connecting a client with the VM via websocket, where there is only a minor difference between receiving a message from another server process or from the client (handle_info vs handle_event). The data/state lives completely in the users' backend process, so much less cognitive overhead about getting data into the client, you actually don't even think about this topic. Also no JS blobs that get bigger with increasing amounts of pages/components or clever splitting + latency compensation of prefetching, and the SSR performance is spectacular, and also free (see the complexity of ReactServerComponents) and no thinking about the whole topic while doing LV.
- under demanding pressure, the VM behaves optimal for a web app actually. Every LV is its own process on the server, every "thing" ultimately lives in some process, and its all supervised by other dedicated processes that do automatic restarts/... in case of failure, sometimes called "self-healing". Erlang was the original system actually achieving the nine-nies of uptime. Garbage collection is also on a per-process level, not a global stop-the-world thing, with short-lived processes cleaned right away without the local GC kicking in. And the VM itself does aggressive preemptive scheduling. This translates into: quick http responses stay quick even under load, but long-running jobs run even longer when there is pressure - exactly what you want: not blocking visitors' experience with crunching jobs.
- the database story is exceptionally great, you have schemas and separate functions to do stuff with data (validations/...), not a coupled thing like ActiveRecord models - I can have different changesets/rules per usecase on the same schema easily. Can also be used for casting JSON responses, and supports embedding document-like structures (with schema/...) into postgres natively.
- many problems you would reach out for external services to solve have native built-in equivalents. IE you don't need Redis, since there is the built-in (D)ETS. which also is objectively better than redis for caching needs, since there is no serialization overhead between service boundaries, and native language structs are used for keys/values, not just strings, and it can be queried/matched with full power. When you know your full Elixir toolbox, your app becomes much simpler on the highlevel (architecture/infrastructure) and more convenient on the lower (code) levels.
- Everything[2] is packaged up (i believe) in an industry-leading level of polish and quality, from exhaustive docs, awesome testing tools built-in (ExUnit for liveviews is dramatically better to use than something like cypress and getting app configs right for testing modes and you have the fullstack integration testing level by default easily.
- the data processing/ML story is generally great and is quickly catching up to python levels, and the unique capabilities of the VM itself unlock tools like Broadway to build data pipelines to feed into things like ML models. Speaking of, some algorithms like NN have great native libs now, there is a adhoc integration with huggingface (bumblebee), and last but not least some things like evolutionary algorithms are great to design with OTP constructs.
- the language itself is as powerful as a lisp in practice, the only noticable exception being its not homoiconic so you need quote/unqote for macros and AST stuff, but also there are less brackets. But everything you see is a macro ultimately, even defmacro is a macro. create your own DSLs with ease if needed.
---
as a downside, deployment is more complex when you want to leverage the distributed nature of some native features, but you don't need to in general, you can also deploy it like any other ruby/django app iE with a dockerfile and loadbalance it traditionally. Also there is a learning curve in general, since all this stuff _looks_ alien for many devs not used to it, and there are features that don't exist at all in more mainstream languages.
---
[1] except for raw compute tasks, when even Nx doesn't cut it, but then there is rustler for native extensions which works reasonable well!
[2] except for liveview itself - the docs are there but cluttered over many places, and not really within phoenix itself but as an external lib, and there has been lots of changes over time.
Is there easy tooling for deploying to something like AWS ECS and having nodes communicate with each other?
I know they support Elixir/Phoenix.
WRT getting them to communicate? I ended up giving up on that altogether…
I’m usually good at figuring that stuff out but this one ended up not being worth it (in my case).
Was your requirement to have ssl as the distribution communication mechanism ?
As a bonus point, no real need for container orchestration as the BEAM just figures it out.
I personally use mix release[2] that assembles a tarball of itself (together with BEAM), then rsync that to Hetzner, restart the remote process and viola. I like simplicity.
Re cluster of nodes, the easiest would be to use a library[3] to automate the formation of said cluster.
[0] https://www.gigalixir.com/
[1] https://fly.io/docs/elixir/getting-started/
[2] https://elixir-lang.org/getting-started/mix-otp/config-and-r...
Our service is primarily a graphql API. The mobile apps talk to our service, and then it internally talks to other services. We use Absinthe for graphql, and a lot of it is subscription based.
Since these nodes (pods in k8s terms) are clustered, each pod is aware of the other pod. Sometimes we get a huge spike in traffic and the clusters can't start fast enough (we're increasing the scale up time in k8s) and a pod can get OOM killed due to number of graphql subscription and then just go in crashloop. This leads to libcluster thinking the node is still good but when infact it's in crashloop, and thus not allowing any new pods coming up to start up.
We're experimenting with few things, but yeah clustering is not without it's pain
libcluster[0] has a bunch of strategies to form clusters. It seems that ECS supports service discovery through DNS, so the DNSPoll[1] strategy should work.
[0] https://github.com/bitwalker/libcluster [1] https://hexdocs.pm/libcluster/Cluster.Strategy.DNSPoll.html
What happens when you have to refactor a large elixir feature on a code base which many teams touch? In such cases you can’t test manually all code paths for runtime errors, so are you just relying (hoping for) perfect test coverage to catch any type related errors you may have caused?
I do want to clear something up. Elixir is strongly typed, but its not statically typed.
More here: https://www.educative.io/answers/statically-v-dynamically-v-...
Even for langs that are statically/ and strongly typed on the BEAM (like Gleam, whose type system is similar to that of Standard ML or OCaml) still subscribe to the "Let is Crash" philosophy, especially when it comes to messages sent and received between processes. The only thing that is guaranteed is your system will fail at some point, how should the system protect its self form that?
https://www.educative.io/courses/concurrent-data-processing-...
In short, its not like working with Ruby or JavaScript or Python, etc..
If you change the shape of a model object and break its type, it’s great that BEAM will gracefully handle that mistake, but no matter how many times that service restarts, it will still be broken at runtime.
I think type systems have gotten so good in modern languages (Kotlin for example) that it’s a disservice to your org to not use a statically typed language.
I haven’t looked at Dialyzer lately, but is it as robust and easy to use as a first class type system? Having experienced partial typing with annotations with python I must say it’s not nearly as smooth as a proper statically typed language.
I do wish something like Gleam was the front runner language on BEAM - I bet it would get taken a lot more readily.
Comparing it to Python's gradual typing isn't entirely fair for a couple of reasons:
1) Elixir is functional, meaning a value's type never changes. This makes it easier to reason about the code and allows tools like Dialyzer to be more effective.
2) The combination of value/pattern matching and the "let it crash" philosophy is unique to the BEAM ecosystem and has proven invaluable for building complex applications. this out side of Dialyzer gets you 80% the way there. (I don't even use Dialyzer in my applications because I don't think it buys you that much. I lean on testing, typically to cover that last 20%)
I'm intrigued, however by the new type system that's being explored for Elixir by Jose and this team. It promises the best of both worlds: dynamic typing with the assurances of a static one.
If we were to impose a Java/Kotlin-like type system onto Erlang/Elixir, there would be several concerns:
It could encourage harmful coding practices that hinder code evolution.
It might conflict with the "let it crash" philosophy, leading to more "nanny coding."
The effort required to satisfy the type system could outweigh its benefits.
It could reduce productivity and readability.
If not integrated into the BEAM core, it might not offer optimization benefits and could create compatibility issues.
It could divide the community and distract from other important work.
Elixir's existing features like testing, value matching, and the "let it crash" philosophy already provide a robust safety net that a static type system might not significantly improve upon.Immutability and pattern-matching get you at least the 80-20 of what a rigid type system does for larger code bases, and your team won't have to be as large when you're using a language like Elixir or Clojure as it would with older, more entrenched languages.
How does immutability or patter matching save me from runtime errors caused by changing the fields or types inside a struct? Wouldn’t the pattern matching just fail and go down the wrong code path since it was matching for the old pattern?
I feel like immutability is great, pattern matching is great, but static typing is also great and there is no reason why it can’t happily live along side the other two.
Elixir structs have compile-time checks: https://elixir-lang.org/getting-started/structs.html
But the problem still exists where clicking outside the modal (or pressing the back button) will destroy the form and any unmodified content, which is super annoying. This can be addressed by listening for changes to the form's input fields and showing a warning when any navigation event occurs when the form is changed-but-not-submitted (easier said than done, but ).
I'm not even disagreeing with your point, really. It's just worth mentioning that 1) many of the problems described have an out-of-the-box solution when using the current LiveView generators, and 2) refinements can be made to address some of the remaining issues.
But as a side note, I got rid of modals in my app, as showing modal on mobile devices is bad UX (IMO). Then, my Elixir code became smaller/simpler too. Win-win.
It's my goto for any hobby projects these days (used to be Django).
Phoenix has undergone some bigger changes with recent versions due to the Liveview development, which were a bit annoying to keep up with but in general, everything is very stable and well documented.
Elixir in general is one of my most beloved ecosystems in terms of develper productivity. - Opinionated, configurable formatter, type annotations and checking, very good test and doc tools, awesome community...
It's everything I know from the Ruby + Rails community years ago but dialled up a bit. I haven't done much Ruby work in the last 5 years so I can't provide a direct comparison but I always felt like you really feel the extra 10% of polish Elixir has over Ruby due to the core teams strong involvement in tooling and developer experience.
The only even more streamlined experience for in recent times was Flutter (as long as you don't stray too deep into ffi/native code)
I always just look at the number of bugs phoenix has compared to comparable frameworks and it makes me very glad to be using something this stable.
If anyone else is thinking of switching from Rails to Phoenix then I humbly recommend my course Phoenix on Rails (http://phoenixonrails.com/). Sorry for the self-promo, but if you use the code HIT8RUN (valid for the next 48 hours) then you can have a 10% discount.
[0] https://solnic.codes/2016/05/22/my-time-with-rails-is-up/
I get the feeling that certain topics about certain technologies often get at the front page even if there is other threads that have more points and activity that isn't shown on the front page.
Why is that?
> How are stories ranked?
> The basic algorithm divides points by a power of the time since a story was submitted. Comments in threads are ranked the same way.
> Other factors affecting rank include user flags, anti-abuse software, software which demotes overheated discussions, account or site weighting, and moderator action.
So if a story gets points very quickly, it will appear on the front page. If it don't keep getting points it will disappear.
So it's basically a game of pure chance, a topic that's not too niche, a topic that one upvotes without even reading, and/or good clickbait technique.
HN's algo optimises for popularity, rather than meaty content.
I'm just speculating here but I think a number of posts are curated to land on the home page when you're directly or indirectly related to YCombinator funded companies.
I explained what is probably going on in a sibling comment. Less of a conspiracy, and more about a handful of people seeing the hyped language du jour in the title and upvoting it in a matter of minutes.
Chris McCord is the creator of Phoenix and this is a post about Phoenix functionality. What you say is likely correct but that's a strong tie.
Probably the right thing to do ethically if you want to run a forum but at the same time it would be bad business.
I'm not saying that this occurred here and I don't really care either but I wouldn't exactly trust a company on their word on something they can benefit financially from.
#liveview-native on the Elixir slack is a great resource as well to learn more.
Last I checked a while ago Brex famously used to be on Elixir but now are on Kotlin.
https://medium.com/brexeng/building-backend-services-with-ko...
With added LiveView on top, you could avoid writing a lot of SPA react (vue, svelte etc) code (some limitations apply, but you can do a lot), spending less people, time and resources for the same result.
Traditional setup: backend logic + backend api + react SPA + logic for calling backend api
LiveVew setup: phoenix backend + LiveView views + a bit of glue code (in elixir) to replace onclick etc stuff
1. Laravel Livewire for PHP
2. Hotwire for Rails/Ruby
3. Blazor for C#/.NET
4. HTMX* as a general tool for any other web server
5. React Server Components (if you squint hard enough)
Phoenix Liveview definitely started the trend by showing a mature implementation of these ideas on the same level as the application developer.
I would argue number 5 is a bit of a meme, since you still have to write a ton of JS, but if you're more interested in the architecture ideas for structuring your app, there is an interesting parallel that I think can be drawn.
[*] HTMX doesn't really embrace the idea of calling a function from your server by name, but the ergonomics are similar when writing frontend code.
Livewires performance is terrible for anything other than very basic components. The fact that it has to rebuild state on every interaction makes it a pretty poor tool for most use cases.
I have other issues with Livewire as well, including how Caleb operates his communities. He has and continues to make a ton of money off Livewire and still provides zero support to the community. Want any actual support? Well that starts at $6,000/year for one support request a month.
He doesn't even appear to be a member of his own discord server anymore. Not that it makes a difference; last time I checked had only ever sent 34 messages in there and the last one was in 20202. He has one guy who would randomly show up around once a day and answer some of the questions.
I don't expect free support but if your package is so confusing that the community is unable to support each other, you should probably be around trying to help sort that out. Instead, he just keeps doing re-writes which puts the the load on everyone else.
Anyways; if you're interested in this type of project I HIGHLY recommend you check out Elixir/Phoenix/LiveView. I haven't enjoyed writing software this much for a long time; not only is the language and framework more enjoyable to work with, but the community is so much better in general.
The syntax reads and writes very well. When I now have to write other languages, mainly Typescript, I miss native pattern matching and the pipe operator.
Whatsapp famously got to 1 billion messages a day with only 11 engineers on the BEAM (the VM that elixir runs on). It has proven itself immensely capable at the highest levels of scale so the only thing for anyone to decide is whether the stdlib and tooling is to their liking (speaking mostly in the context of networked IO bound applications, which is most things on the internet)
People are of course different and I fully acknowledge that things that seem normal to me may seem odd to others, so YMMV.
So you were correct some years ago but I am not seeing this nowadays. So yeah, valid criticism, at the same time this criticism has been mostly taken to heart and addressed as of today.
also, the errors you get, when they are in erlang are in valid erlang, i can't say that about Java, Ruby, Python or PHP which give you errors in English.
https://hackernoon.com/why-is-erlang-the-only-true-computer-...
Fans of Elixir out in force today downvoting dissenting opinions even though its valid.
In almost every lang you'll run into something that your aren't familiar with and you'll have to dive into something you aren't comfortable with. I had to dive into some C code when ruby decided to segfault. I've had to dive into Java JVM internals when working in Clojure.
So I'm not sure this is a "elixir" thing as much as a "I'm a developer thats supposed to know multiple languages and stacks" kinda thing.