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

You have to draw the line somewhere. I think that in liveview’s case, the abstraction level is pretty OK for most of web needs. Obviously if you venture into the hooks and frontend logic territory, all of this becomes a mess really fast. You can usually spot liveview projects that are bound to fail by the number of hooks they use.

I am not entirely sure about that. Yes phoenix provides firsthand integration with ecto, but I’ve had cases where I was using ecto embedded schemas to get the validation power without a need to couple to a database.

I am a big fan of SQL and database engines such as postgres for multiple reasons, but it’s clear as day that distribution is not one of its strong suites. I am actually not a big fan of key/value stores or nosql databases such as mongo, as a lot of logic that was implemented and standardized by SQL you will have to do by hand in your application code. At my last job I’ve had to work with mongo and the only thing I can say that it is a much worse version of postgres, those JSON queries they have are horrible and overall performance is too.

As for code organization, I think that it’s possible to make good boundaries in your application and to not sprinkle ecto stuff everywhere. I understand your frustration and I have personally worked with codebases that failed to do that and it can get hairy. I’ve also worked not so long ago in a codebase with 20K+ source files where the queries were neatly contained in their own boundary, but that project is beyond what solo developers can build, as it had a ton of various custom linters and credo checks to ensure this consistency.

2 Likes

Do you, though? I mean I understand this is radical thinking, but take a step back and try to envision what would happen if you simply chose not to draw the line at all. I am not saying this would be easy - quite the opposite. It’s really, really hard. That’s why almost no-one has gotten it right.

I was careful to mention my admiration for the relational model specifically because I wanted to disclaim any association with NoSQL/Mongo and so on. There is no question that the NoSQL movement threw the baby out with the bathwater. And probably the bathtub, and perhaps the bathroom as well. Database companies in general have strong incentives to lie and few incentives to produce useful products, because doing so would require them to dogfood their databases on real applications, but they don’t make real applications, they make databases. It’s not a great feedback loop.

But look at how real applications are structured and you will see that modeling them on top of databases like Postgres is a fool’s errand. Think apps like Figma, Linear, or Notion.

Ironically I believe the last two were built on top of Postgres, but if you go looking for their writing on the topic you will realize they have tortured the database beyond recognition in order to do so. Linear in particular essentially built an entire database using Postgres as a storage engine. At which point you have to ask yourself: is Postgres a good storage engine?

No, actually, it really isn’t.

:rofl: right on the point

This argument of “real applications” is getting tiring honestly. In my books what qualifies as a real application is something that brings value and was built by investing resources smartly. I like to always quote this:

Any idiot can build a bridge that stands, but it takes an engineer to build a bridge that barely stands.

I also think that a lot of these arguments are getting beyond the discussion at hand, namely about small scale projects built by solo developers. You talk about some huge products that are pushing technologies to the limit. Those products not only don’t share the same problems that small applications have, but they also can afford to hire teams to create their own technologies in-house, this is what google, amazon, facebook (and telecom industries before them) was doing for years.

1 Like

You can call them what you want, I am just referring to the types of applications that I want to build and use, in contrast to the types of applications I often see or find myself using today. I don’t have a better term to use. Perhaps “desktop-class applications on the web” or something, but that’s a mouthful, you know?

I strongly disagree. First of all, I don’t think any of the three applications I cited were that large when they started. A huge amount of the engineering talent at Figma for example was Evan Wallace (who also soloed esbuild). I strongly doubt Linear or Notion started with more than a couple engineers either.

But more importantly, we need to build technologies which allow small teams or solo developers to “push technologies to the limit”. That’s the goal, I think.

Finally, I would like to point out that large companies totally failed to build anything like those apps I cited. Apps from large companies suck. Adobe was literally willing to shell out twenty billion dollars to buy Figma because they could not replicate it (and they tried, remember).

I am presently a solo developer trying to build my own technologies in-house, in Elixir, while using LiveView. I feel this is on-topic, but I am open to hear any disagreement from the OP.

2 Likes

yes :heart: maintaining coherence between the contracts is the most difficult part

1 Like

I have this goal as well. I just call them “instant apps” because every interaction feels instant. I think only a local-first / offline-first app can achieve this. It didn’t seem like LiveView is capable of it and actually seems like it’s the opposite since the client will keep a lot of state. Would love to hear more about how you’re going about it.

1 Like

I was not referring to offline capability or local-first, though I agree those paradigms are interesting.

By “desktop-class” I am referring to the user experience that desktop applications used to have. Functionality like undo/redo, drag/drop, the idea that things “stay where they were” as opposed to being reset every time you open the app, and so on. None of these actually require local-first, but they do effectively require stateful, declarative (reactive) UI toolkits to pull off. They also require you to structure your “backend” (database) in a very particular way, which I have found exceedingly difficult to do on top of Postgres, hence my constant complaining on the topic.

But again: LiveView is actually capable of expressing these things. I can prove it, I promise!

Client rendering and local-first are certainly long-term goals for me, but starting there is like running before you can walk. Most webapps, React or not, are not even capable of basic undo/redo or any of that other functionality we used to have. React-style frameworks are table-stakes for being able to implement that functionality, but you still have to actually know what you’re doing, and there are essentially no resources to teach these things either. It’s an unfortunate situation.

Also, for the record, LiveView latency is almost unnoticeable if the server is actually close enough. The problem is that if you run multiple application servers in different regions they are still connecting to the same database, so your latency is not improved for most actions. Note that this is often true even for client-rendered apps!

So if latency is your priority, your first task is figuring out how to split the database up and shard it around the world. Only after figuring that out should you even consider worrying about client-rendering IMO.

So you can see where my priorities lie!

2 Likes

Cool, would be great to see how you approach it when ready.

Interesting. This sounds a bit more complicated than a client-rendering / optimistic ui approach. Maybe not?

1 Like

Well client-rendering alone solves nothing because you are still hitting the database on most operations. Unless you have an app which does not need a server at all, in which case obviously none of this applies.

If you are hitting the database then counterintuitively LiveView is often faster because it keeps a websocket open (most client-rendered apps don’t do this) and it tends to be easier to make several queries to the DB at once when your state is on the server. These are not hard rules per se, they’re just how things generally work out. Very careful backend API design would obviate most of the difference, but there is still no advantage for client-rendering if you have to hit the DB anyway. This point, by the way, is one I have seen made by the Phoenix team (e.g. Chris, or maybe it was Jose) in the past. And it’s spot on.

Now once you get into optimistic UI territory, that will of course come out on top for latency. But the simple optimistic tricks many are familiar with only work for simple interfaces. This is what I was getting at when I mentioned modals earlier.

To get optimistic functionality with complex interfaces you need to build it very deep into the model and application. Now you’re in local-first territory: CRDTs and so on. You don’t literally need CRDTs (Figma for example cheats), but you need something which is roughly equivalent in complexity anyway.

Obviously once you’re in local-first/CRDT territory you have something which can definitively beat LiveView. I fully agree that is the best possible approach. It’s just that I’m pretty sure we can beat the user experience of 99% of client-rendered apps with LiveView, simply because most webapps aren’t very good regardless of the tech they’re built with.

One more point: there are some things which are inherently server-side and cannot be accomplished even with local-first. For example, my product is a feed reader (RSS). You cannot fetch RSS feeds local-first; that doesn’t even make sense. You have to hit a server, somewhere, to refresh a feed. By definition. So the location of the database still matters in that case, even if the app is 100% CRDT magic.

1 Like

I’ve been running a profitable, steadily growing SaaS for the past eight years. The stack is Phoenix on the backend with Vue.js on the front, and I’ve loved how productive that combination has been. In my day jobs and contracts I’ve also spent more than four years working with LiveView and Tailwind, so I’ve seen both approaches at scale.

I simply wouldn’t choose LiveView for any new project. The ecosystem is the sticking point. There just aren’t mature and widely used component libraries for LiveView. Yes, Salad UI and Fluxon exist, but they’re very basic: most of their parts are little more than styled, stateless wrappers. Whenever a team needed data-rich, stateful components—think interactive tables with built-in sorting/pagination/row selection/expansion etc., calendars, menus, steppers, or autocompletes—they ended up building them from scratch or using one-offs like LiveSelect that have a lot of warts. This inevitably slowed development to a crawl and often left the team with a brittle tangle of frontend code that nobody was eager to maintain or troubleshoot, much less build upon.

I use Vuetify for my SaaS, and the value it provides is astronomical. Vuetify gives me fully tested, production-ready components that handle state, emit clean events, and slot right into a Vue app. All the Phoenix backend has to do is send JSON; Vuetify takes care of rendering, interaction, and polish. It feels like having a dedicated UX and frontend team on staff, which is priceless when you’re a solo founder shipping a feature-heavy product in an industry with well-funded competitors.

Because of that gap in the ecosystem, the claim that LiveView is a great choice for solo developers doesn’t hold water for me. If I’d tried to build my current product with LiveView, I doubt I could have reached the same depth of features—or done it without burning out. Vue plus Vuetify lets me move faster, keeps the codebase maintainable, and lets me focus on what actually matters: shipping features.

Getting a SPA to work with Phoenix is quite painless these days. My app is a mono repo. Vue code lives inside the assets folder, and Vite (and previously Webpack) is configured to compile the assets into priv/static, from which they are served and mounted into index.html. That’s it. Architecturally, keeping this separation of concerns between server and client has led to significantly less complexity than every LiveView project I’ve worked on.

One other thing I want to mention: if you foresee yourself needing a mobile app, you’re probably going to need a JSON API, so you might as well use a SPA. That way you don’t have to maintain two different interfaces. Having a unified API and letting each client do whatever it needs with the data it serves is going to save you a massive amount of effort.

3 Likes

This is an entirely valid perspective, and it’s something that applies to Elixir in general. We’re a small community and there is not much pre-built stuff to go around, so you have to do a lot more yourself.

On the other hand, if for whatever reason you prefer to build things yourself, it’s fantastic. There are no circumstances under which I would use third party components anyway, so this sticking point is completely immaterial to me, personally.

This is a good point, and I would say this is true in general. If your app works as a mobile app it would probably also be happier as an SPA.

I know there is work on LiveView Native to come at this in the other direction, but I am not sold on that approach. I think it violates user expectations a little too much. I could end up being wrong.

While the above is true from an engineering point of view, I doubt it is applicable here. As a solo developer, it is better to stay 100% web or 100% mobile. On the web, there are many good reasons to go with a client side first approach but I wouldn’t make any architecture decision base on wishful thinking.

1 Like

A lot of the times you don’t get to make that choice. If your competitors are offering mobile apps for their SaaS platform and you aren’t, that’s going to put you at a disadvantage. This is especially true if you need to support multiple user types (e.g. those who work from home or office, and those who work from the field).

I don’t have enough experience in developing a SaaS business so I cannot counter this argument. All I can say is that I have not seen good user experience from both the web and the mobile version of the same app from solo developers or very small teams.

Sure - I’m simply countering your original argument, which is that requiring different clients is wishful thinking (in your words) and one should stick with either 100% web or 100% mobile.

You seem to be making a different argument now though, which is that it is difficult to pull off good UX across multiple platforms with a small team. No disagreement there.

1 Like

I think that the main point was the fact that most folk doesn’t focus on UI/UX (and especially mobile) as solo developers, which makes a lot of sense from what I saw in most of the small projects I’ve been part of.

Usually the projects I was part of were value oriented around solving/automating business or technical problems so my opinion is obviously biased, but I can say that the UI part was usually the smallest (and least significant) of the problems in all of those projects. One of the example projects was a real-time telecom fraud detection map that would triangulate the position using tower maps and events coming in real-time. Obviously the websocket connection liveview provides was an amazing tool to have at hand, which allowed us to deliver a fully working prototype in about a month. I can say it without thinking twice that this would not have been possible using another stack.

There are countless use-cases and I wouldn’t dare to say that liveview is a universal choice, as this is very far from truth. A good engineer can assess the situation and make the correct choice based on the requirements at hand.

1 Like