Seeking Experiences with Phoenix, LiveView, and Elixir for Solo Developers and Small Teams

well the thing is with lower level programming languages you get to explicitly state it in your code if it’s pass by reference … not in js that’s the default

maybe the reason why it seems more logical to me is that i have been considering mutations as an anti pattern already previously, if i want to change an array i map over it return a new array and reassign it instead of mutating it directly

So it does make sens for me to chain a bunch of functions and return / assign the return of the transformations instead of chaining a bunch of methods to mutate an obj, React code bases are a bit similar to functional programming …

if we’re being honest 80% of web apps are just forms buttons tables and cards :joy: especially for indie devs & smaller teams, we’re mostly just building a pretty ui ontop of a db & some storage with a bit more logic for auth & authorizations, maybe some charts here and there … i’m realistically never going to be building something huge at least not alone … and if i do get so much success that i do need to make it into a huge thing well that’s a perfect problem to have …

2 Likes

For sure, everything has tradeoffs. Depending on the app you’re building and the space you’re competing in, you may be able to get away without having as much polish or deferring it. I think the ux bar has been raised considerably to the point where it’s difficult for me to forego it.

1 Like

You say that and for some reason more than 90% of all sites don’t respect even basic rules when it comes to accessibility.

2 Likes

i tried using different sites made using phoenix liveview and the UX was really good

1 Like

It really depends on what sort of UX is expected, but you can often do more nice stuff with client-heavy forms. Like, you can handle individual keypresses, without having to worry about round-trip latency back and from the server and possible additional keypresses in the meantime. It’s quite difficult to get right with a LiveView, having an equivalent of “controlled input” in React, without writing JavaScript heavy hook. And I’d rather write a React component, which is a predictable and functional in style and spirit code, rather than dealing with race conditions between client and server states.

Client-side rendering is quite good for forms, tables, cards IMHO.

2 Likes

My team is less than 10 engineers. We’re just starting to use LiveView for new pages and also rewriting some old Rails pages / features.

People seem to really like it. I think it’s especially a good fit for us because our pages don’t need super rich UI interactions. I think we only have about a dozen Phoenix JS hooks, and it’s all just vanilla JS.

The Rails features that have been ported over are massively simplified. Not just in the LiveView, but business logic also. I can’t quite articulate it, but there is something much more simple about data centric vs object/class centric.

4 Likes

i’m looking for something that can help me speed up the development, to be franc i cannot image a scenario where i’ll need to listen to key events and if that shows up :

document.addEventListener('keydown', (event) => {
  console.log(`Key down: ${event.key}`);
});

document.addEventListener('keyup', (event) => {
  console.log(`Key up: ${event.key}`);
});

same thing with controller inputs i can just

<input type="email" id="email" placeholder="Enter email" />
<small id="error" style="color:red"></small>

<script>
  const email = document.getElementById('email');
  const error = document.getElementById('error');

  email.addEventListener('input', () => {
    const value = email.value;
    if (!value.includes('@')) {
      error.textContent = 'Invalid email';
    } else {
      error.textContent = '';
    }
  });
</script>

as i said above what matters to me the most is the speed of development, if a tool allows me to be faster with a good enough result than i’ll pick the faster tool everyday

until now i have not seen any fullstack js framework that allows me to generate a complete crud + auth with two commands, now yeah i can generate that and call the api but why? now i have a bunch of glue code i need to write, why not just have the interactivity baked in… someone srsly needs to make a rails for react, the closest thing to that is inertia… but even with that there still things missing like pubsub & real time features, but i think the reason why no one does is because each new react adds so many new things that shift the entire way we do web apps from class components to functional components to RSCs & server actions

i get it that you get a more declarative way of managing state with js frameworks, but what for? having frontend decoupled from backend ends up slowing you down so much…

for example when using flowbite modals they listen for escape key to close the modal by default and you can turn on or off that behavior with attributes & js… i’m not gonna write the logic for my modal myself i’m just 1 dude i’d rather focus on the things that matter → the business logic, not a key press, i don’t care so much about a ton of interactivity, i just need enough of it for the experience to feel smooth, anything more is in my opinion overkill & counterproductive, because if i spend more time on these things i’m taking away from the time it takes to figure a way to build something that is useful for a client, i’m solo

as a solo i need to be the backend dude, the frontend dude, the dev ops, the sales & marketing guy, the customer relations guy, the designer… everything needs to be done by myself, so the more i can off load that to my framework and tools to handle it for me the more productive i get… @D4no0 what do you think of that statement

3 Likes

Just go for it, you will not regret it. The only advice I can give is to avoid abusing hooks, like for example the one mentioned above to avoid round-trips to server, that is a unnecessary optimisation that goes against the logic of server-side controlled UI.

1 Like

i would argue sales & marketing & customer relations have a higher impact than the actual product for solo devs

1 Like

yeah i’ll just use the submit event instead of controlled inputs, when using react vue or other js frameworks we tend to override the default html validation with js using two way binding with the input, i don’t feel that is necessary at all when we have ways to do that using html already + we will need to do server side validation anyway with or without the client side validation…
the phx-change="validate" form helper also is really nice
as chris mccord said, in most cases the thing you cannot get rid of is the server, so putting the state on the server drastically simplifies everything instead of putting on the client, in fact the closer the client can mirror the server the easier things get and that is exactly what liveview does and that’s what i really like about it
obviously some apps are more client heavy than server heavy but for those liveview is not the best choice…

Express, Next.js, React Router v7, NestJS, AdonisJS, SvelteKit, Nuxt, TanStack Start, Supabase, Firebase…
Where do you even begin?
If you use ts in the frontend than it’s a bad decision to not also use it in the backend otherwise you’d not only be decoupling the app but you’ll also need to write the types twice every time or use some custom rpc or graphql or some open api swagger thing …
Is your app running in Vercel, AWS, Cloudflare workers ? or self-hosted? To ssr or not to ssr ?
Which ORM should you pick?
Should you even worry about cold starts?
Use nextjs cache ? Oh sh-t the old way of doing it is deprecated now there’s a ''use cache" directive, or maybe cache on the client side side with tanstack query, or maybe redis
How about cron jobs ?
And how about web sockets ?
Context vs redux vs zustand ?
Ah sh*t you’re hitting the payload limit now you need to change up your uploads to presigned urls
What validation library makes sense? Does it work well with shadcn & Typescript ?
What about auth, does the solution you chose even have an adapter for your framework and ORM & db combo?
Then there’s storage, hosting, services, and a dozen shiny startups popping up around all of this.
Three months later, half of your stack is obsolete because the “next big thing” came out, and you’re back to square one, relearning everything from scratch.
It’s exhausting.
Eventually, you hit analysis paralysis and stop building entirely, because you’re too busy reinventing the entire universe instead of building something useful.
Meanwhile, 70% of the web is still quietly running wordpress, PHP, jQuery, and Bootstrap hosted on a simple vps… :sweat_smile:

Contrast that with something that has everything baked in, from a to z, front to back, you have everything you need, and you can focus on building rather than reinventing the universe → that’s what i’m looking for

2 Likes

This is Meteor.js. I haven’t seen a more productive js framework. You can use react with it or, if you want to avoid react like me, bring your view layer of choice — svelte, vue, solid, etc.

In general though I agree with you on js framework fatigue. It sounds like LiveView could be a good fit for what you’re looking for. I wanted to use it exclusively but after some experimentation it didn’t seem like a great fit for my use case. Maybe it will get there in the future.

1 Like

Thanks for the suggestion, I checked out the demo ToDo app on their site, and honestly, it didn’t click with me at all. I’m also not a fan of MongoDB as a default choice. The demo didn’t really showcase the kind of structure or capabilities I’m looking for.

Funny enough, this actually reinforces the point I made earlier, in the JavaScript ecosystem, there are rarely solid defaults. Instead, you get endless fragmented libraries and frameworks that all try to solve the same problems slightly differently, constantly reinventing the wheel.

It’s kind of hilarious that their demo includes a deployment section, but nothing on data validation or schemas. I get that MongoDB is schema-less by design, but that’s not ideal for most apps. In reality, most data is relational and benefits from a strict schema. You can find mentions of Zod and other validation libraries if you dig deep enough in the docs, but it’s not front and center. And still, no real mention of Postgres or MySQL.

Also, there are things that JavaScript just can’t do as easily or effectively compared to the Erlang VM. Take Phoenix, for example, the reason it can maintain so many open WebSocket connections is because of BEAM’s lightweight processes. Try doing the same thing in most other languages and it collapses fast.

And clustering, how does that framework handle it? I’m not interested in relying on their Galaxy hosting platform.

LiveView just wouldn’t work in most other languages. It thrives in Elixir specifically because of the BEAM. Not many platforms offer that kind of concurrency or fault tolerance.

Contrast that with Node.js, it doesn’t utilize all CPU cores out of the box. If your Express server crashes, it stays down unless you explicitly set up a process manager. Meanwhile, the BEAM VM handles that with supervisors by default, raise an error in LiveView, and a new process just spins up effortlessly.

1 Like

To be fair, I have two private applications in charge that are made with Vue on top of Phoenix Channels and for which going to Liveview for the interactive part would be a tad too much effort. I would be doable, but as @hubertlepicki outlines, the optimistic updates would be a lot to manage to get to the same result.

But for most of the others I benefit a lot from what Liveview gives me in terms of scriptable UI, and I write my fair share of hooks for low-latency interactions. But the server always holds the truth. JS interactions are used to simulate instantaneousness.

There are thresholds and you need to evaluate them for your own app. From your description I’d go with liveview though. Don’t fragment state management all over the place and only use JS commands / hooks for transient states that do not need to be shared or persisted.

Should a drawer “open” state be part of server state and persistable ? Today the answer is mostly “no”, but if you think about the usability of desktop apps, you often find the UI just as you left it : configurable, adaptable… So I’m not a fan of toggling things in a transient fashion for UIs that are made for an user to work long sessions in.

2 Likes

As has been mentioned by a few others I would opt for for something other than LiveView on the front-end to start with. That gives you the most flexibility. Once you are comfortable with Elixir/Phoenix than you could re-evaluate and see if it make sense for needs to go with LiveView. I’ve gotten to the point now, after a decade of doing Elixir that I always default to LiveView, but to learn Elixir, Phoenix, and LiveView all at once especially when you are new to the ecosystem is a lot to take on. I was recently working on a project that had an Elixir/Phoenix backend and a NextJS frontend and decided to rewrite it in LiveView and it was the best decision for the project. There are other projects where it probably wouldn’t make sense to do the rewrite. Just depends on context.

3 Likes

All good. As far as a batteries-included js framework for solo-dev productivity, it is unmatched imo. They might not be advertising the things you mentioned as missing well enough at first glance. But you’re right, mongo may not be a great fit depending.

Agree with your points on the BEAM. It and Elixir are what drew me here so you’re preaching to the choir there. :slightly_smiling_face:

I think you should give LiveView a go and see how you like it. If it’s not quite what you were hoping for, you can always use channels with a SPA or inertia.js and still take advantage of Elixir and the BEAM.

2 Likes

I agree, most definitely. A drawer should be client-side only, but the thing is, I take that as a given. I often use Flowbite or other JS libraries that use data attributes to hook into the HTML with some JavaScript. Flowbite, for example, has drawers that can open and close independently without any server interaction.

The only time I involve the server is when the drawer is tied to a specific entity, for example, an edit/create product drawer. When looping through the products, I’ll add some js that cleans up and re-runs initFlowbite, which uses the data attributes for the JS behavior. That way, when a user adds a new product, it updates the table or cards, and also initializes the drawer for it.

The key point is, this doesn’t require a JavaScript framework, just data attributes.

Yeah, Inertia is really underrated. If I were to use a JavaScript framework, it would definitely be Inertia, it deserves more praise than it gets.

That said, having a bi-directional line of communication between server and client, like with WebSockets, is incredibly useful. Even with a JS framework, I’d probably still set that up, or at the very least, fall back to polling.

But honestly, LiveView feels like it solves all of that with way less complexity.