Been using Liveview in production since 0.1.0. The cognitive overhead is much lower and I can’t say enough good things about it. Simply put, it made a herculean task of building desktop-like software, merely difficult. That is truly innovation. Fire And Motion – Joel on Software covers my thoughts pretty well on this. While everyone else is busy keeping up with the tech and wasting their time, liveview just works.
Here are some notes:
I designed the frontend so that the user rarely has to hit the server unless they actually new data, so I am not running into any of the downsides of the approach at the cost of sending 10k-20k extra uncompressed data upfront. The compressed page is still 30k though.
Each livesocket uses some amount of memory on connection and holds it in the state. Early on, I had to manually hibernate the threads. This was added by default in 0.5.0. This is super-important to be able to scale the number of concurrent users. I made sure that most of the user actions do not go back to the server by using hooks and css, so this memory saving is real. More on that later.
Security - it took a while to figure out the difference between the different mount options in liveview. For instance, to have the user_id available with both liveview and non-liveview, I had to use conn.assign and Plug.Conn.put_session and read the session from the liveview.
Speaking of sessions, I have been using cookies to store the session and include user preferences. I was shortening the cookie fields from “pref” → “p” in the cookie to save space, but what I didn’t realize is that everything would end up in the session cookie. I’m not sure how I missed it, but I was awfully close to running out of cookie space. Eventually, I had to make a guid in the cookie, store everything in ETS, sync the ETS prefs to DB, and make a distributed cache update on ETS for multiple machines if a pref was changed. Quite frankly, the distributed cache update was the easy part.
Alpine - on that note, I’m not sure what the point of using Alpine is. It just seems to add another layer to have to debug. I tried it and then re-wrote what I needed in maybe 50 lines of hooks.
Form recovery is important to get right. If a user changes to a different tab, what will happen is eventually the browser will remove it from memory on mobile. Form recovery gets called quite a bit for me.
I’ve been strictly using stateful components. The rendering time is in a single process and I haven’t tried improving it. My main page has about 15 components which take 40ms to render. I’m not sure if changing these to liveview would make life more or less complex. I’m still on 0.15.4. Unfortunately, I’m not looking forward to 0.16.0 since I have to change all my stateful components to heex.
Testing - I haven’t written a single liveview test. There have been occasional small breakages on upgrade, but those would not have been picked up anyway by tests.
Rendering size - websocket is passing about 40k-70k of data on an update. Adding compress: true to the socket options saved me about 50ms on update for far away users.
Hooks - Overall, I have 350 lines of js. The user doesn’t need to contact the server unless they are actually executing an update or need a re-render. ’
Typeahead - there are so many tutorials that use datalist including the default from the liveview examples. DO NOT DO THIS UNLESS YOU HAVE EXACT MATCHES. Each browser implements datalist slightly differently, but more importantly, it will further filter your results based on a String.contains. So if your result doesn’t have a comma, but the input does, the result won’t show. Instead, use relative_div → absolute_div → ul / span / etc and use phx-click and careful with safari.
Safari is annoying in general. Not to do with LiveView, but sometimes items where not clickable with phx-click. This is a Safari’ism, where you can’t put phx-click on a div, etc.
Breaking changes - it’s 0.x software which is remarkably stable. But there will be breaking changes on upgrades. I suspect the churn is still less than the js ecosystem though.
Sending events on the backend is great for GUI design/updates.
Regarding fly.io, I’m planning to use it once I migrate a lot of my read only data to sqlite. At that point, the update latency will be ridiculously low.
Yah, what can I say really. Liveview is fantastic.
I’m a one-man shop, having grown quite a bit tired of NodeJS / VueJS endless changes in direction / way to many packages required to make a web app (half of which, only used for development) and a number of other things like testing (i’ve used Mocha and Jest, both of which remain a pain to use IMHO) which all had a part in my decision to go for Elixir/Phoenix/LiveView. Considering the app i’ve moved over is about 75% of my incomes, and a sizeable amount of clients which i all know personally and we’re relying on me to not mess that up, i didn’t take the change lightly (obviously). But it really was all about seriously alleviating the load required to update and maintain my app (and by extension, my happiness in getting up and doing my job which was far from stellar prior to the change) while making sure for my clients that the change would be mostly invisible and/or at least only for the better.
About a year ago (June 2020), i started the rewrite, choosing to learn along the way everything i needed (including LiveView). After all, an actual app where i knew what i had to do, but not really how to do it in my new stack was a good way to learn (and i’ve always done it this way in the past, without much issues so…). I won’t bother everyone discussing everything since it’s about LiveView. 3 months ago, i finally put in production the rewritten version (i use Scalingo) and since then, and i have barely got any issue at all:
I didn’t hit any latency issue, and i’m relying quite heavily on client/server trips (data driven, and realtime saving). To be noted that all my clients are situated in the same country (and they’re all within 1000km from the servers), so i’ve never had to deal with anything related to really long distance usage. I already had to do roundtrip to the server on my previous stack, and the move to LiveView only made it simpler while keeping the performance and optimistic UI.
I’m only testing my LiveViews for things like authentication/permissions, and a number of critical things. Overall, i didn’t feel the need to go far on that front.
One of the thing that ended up being an absolute breeze compared to my previous stack was how to handle the role/permissions system. Well, most things ended up being easier to handle, but that was strongly the case for that part. To put it simply, entirely handling the state server side while keeping the SPA type of app for the user has been an absolute breeze of fresh air. I no longer have to think twice about my data.
I’m not currently a big fan of Live Components, i have quite a lot of them (both stateful and stateless), but they are clearly not (yet) on part with what i had in VueJS. Lack of a proper slots system, inability to use handle_info in stateful components, which makes separating LiveView in multiple parts less easy, or sometimes, not even making much sense. In addition, using non-routable LiveViews has often limitation that come in direct contradiction with the needs, ending up also not being the solution. They’re still good overall, but there’s some things that could be better.
The latency compensation regarding automatically adding classes isn’t of much help currently. Far too often, the element the class is being added to isn’t the one that should receive it (and the concept of the global handling with phx-page-loading isn’t great). I ended up using mutation observer to handle user feedback on what’s going on (adding classes manually where they should be, through AlpineJS init()). It’s not like a big issue, but there’s room for improvements.
I’ll resonate with what @tj0 said, LiveView is fantastic . I know often the discussion is about big companies using the stack you like, but i’ll say that at the very least, for small teams or like in my case, single developer, LiveView seriously ease the amount of work required to make and maintain an app that requires SPA-like stuff. Over a year down the line, i can only say that i’m fully satisfied with my choice and i’m starting to look towards contributing back to the community (still have to get past the damn impostor syndrome, but i’ll get there ).
No, i don’t have any public link (yet). I’ve always relied until now to more conventional (old-school ) ways to get clients (like i said, i know all of my clients personally). It’s probably going to change in the future (the public link, not the personal stuff), but that’s not really a priority.
I’m usually not “comfortable” with things like talking on something like a podcast (not that i ever get the offer to do so), but that’s one way to help the community, so i’ll be down to try. It won’t be right now though, as i’m currently dealing with a lot of stuff (both professional and personal). It should cleared out by the end of the year / beginning of next year, so if you’re still interested by then, i’ll make the room for it
Thank you @tj0 and @shad for the detailed and precise feedback coming from your dev and prod experiences. I have taken notes on what works and what could improve. If all feedback was written like this, my life would be much easier.
I’ve been using LiveView since 0.14.8 and am currently using the 0.15.7 with Metamorphic in production.
I just relocated, absolutely fried, and am still settling in (working on boxes right now) from my last post, so I may forget some details of how I’m using LV but essentially:
The whole app is LiveView with a few exceptions, like the log in page, but all major features are LiveView based (including the video chat through a WebRTC peer-to-peer mesh architecture). This is expanded from Jesse Herrick’s wonderful blog to include authentication and tie-in more with the specifics of a person’s account on Metamorphic.
I’m using LiveView with ETS to handle in-session, temporary decryption of people’s images (for our Memories feature). I first noticed an incredible amount of latency (more than several seconds worth) on my first pass, but then switched to storing only the decrypted binary in ETS and that’s provided a significant speed boost. Now, the latency appears to mostly be coming from the initial call out to AWS to pull down people’s encrypted files (they are asymmetrically encrypted with people’s password-derived keys so that Amazon or any future cloud provider never receives anything but an encrypted blob — which is then encrypted with their own provided encryption). I’m not currently compressing the ETS tables but it is something I’m considering if I see space issues in the future.
In terms of improving the call out to AWS, I keep coming back to finch but, alas, I’m too fried at the moment to understand how I might leverage it to improve the speed there (but something tells me I will be switching to something like req in the future).
I’ve noticed stateful components for the settings section of the app, which is divided into tabs, have a significant load time but I haven’t had time to test and dig into this more yet.
I supplement LV with AlpineJS (currently a mix of v2 and v3) as noticed on the opening invite page. Alpine is also used for things like dropdowns. Overall that experience has been incredible, and I use it with LiveView to talk to a GenServer for our passphrase generator (which uses the dice strategy from the EFF).
For hooks, I really try to lean away from hooks whenever possible just because it’s so intuitive for me to write only in LV. However, I do use hooks for our Portals’ WebRTC, Stripe payments, and TOTP modal.
As @tj0 notes, Safari often behaves differently and one funny moment of panic came when testing the production site on the road on my mobile phone and discovered that the site wasn’t informing me that I had successfully (or unsuccessfully) confirmed my email via the email link. It turns out that if you enable “Block all cookies” in Safari, it does just that — even the functional ones! Since, I use session cookies through phx_gen_auth, this setting was throwing the site for a loop (but it still didn’t crash). The episode also inspired me to write a short blog piece on Metamorphic’s cookies and turn off “Block all cookies” in Safari.
Another great thing about LV/Elixir - I was running on autoscale for awhile with my hosting provider to scale up and down instances of the app, when I suddenly realized that I hadn’t configured Metamorphic for clustering. Suffice to say, some strange behavior occurred (missing messages) but, again, the app didn’t “crash”. I have since updated and fixed this and we are set to autoscale (things like this occur when programming in between raising a little one).
I’ve also been considering Fly.io, I’m just hesitant to make the headspace to learn the necessary ins and outs (docker, etc) and my hosting provider has also told me that multi-region support is on their todo list. But, again, Fly’s case for LV is extremely compelling and people will be using Metamorphic from around the world.
Weird behavior - typically I notice weird behavior occurring between LV and webhooks. I’m currently noticing that sometimes the server-to-webhook-back-to-server will get out of sync (typically an issue with internet connectivity) and then the client-side will act as if their action wasn’t successful when in fact it was.
Testing - I’m mostly testing only really critical things (payments, authentication/security/encryption, authorization, password generation, major features) at the moment. However, I am performing real-world testing continuously. This is much slower and more cumbersome but it helps me see what it will be like for someone to actually use the app (it also often inspires new ideas or changes to existing things as I see how they look and behave in real-time).
Emails have been incredibly annoying to configure for HTML with Tailwind (using the latest). When I test the built-in HTML preview client with Bamboo the email looks exactly how I want it to look. But, when I perform a real-world test, I notice that my email client is stripping away the HTML and my button is no longer an awesomely styled button but the base HTML. I’ve since opted to just style the emails simply until I can return to figuring out the discrepancy that is occurring.
I’m currently at the early stage of the Early Access phase for launching Metamorphic, so not everything is completely ready yet (I’ve had to settle for 3 main features at launch rather than the current 8 that are on the drawing board).
If you visit the site, I am now taking sign ups and will be slowly rolling out invite emails to people who’ve signed up in the next several months.
But, some things currently working on for Early Access:
Adding a TURN server to the STUN implementation of our video chat Portals.
Updating LV to 0.16 (the new HTML engine is both exciting and daunting in terms of upgrading all of the existing files)
Improve and expand my automated tests
It’s also unclear how my ETS strategy for handling in-session, temporary decryption of people’s photos will perform under heavy use. However, I’m currently running on very under-powered production hardware (only 1 cpu) and plan to scale that hardware as the number of people using the service grows.
I’m growing bleary-eyed so I’m going to leave it at that. But, overall, I am definitely all in on LV. It’s such a wonderful programming experience (and looks to be improved with 0.16 from the changelog).
Just to follow up on my own post, I’ve been updating LV to 0.16 and it’s simply amazing!
Without changing any of my existing code I’ve already noticed a significant speed boost to the load time for the settings section of Metamorphic (with the tabbed components) that I mentioned was loading rather slowly previously (speed increase has been seen even on my current Early Access single CPU production environment).
Also, the ~H is just incredible! I was playing around with switching back and forth between the ~L and ~H and removing HTML tags and seeing the ~H render an error with a clear and specific solution letting me know that I forgot, for example, end of file reached without closing tag for <div> or missing a closing <h1> tag on line 24. If you’re not using the ~H then you can make those little typo/omission errors and you won’t be alerted to anything wrong in the browser.
I know everyone’s been talking about these kinds of improvements, but to see it in action is simply magnificent.
Thank you again to everyone in the community and on the teams making these frameworks/libraries/languages so amazing.
Developer happiness + experience just got a super boost with LV 0.16, thank you!
To be honest, I wrote this after reading underjord’s blog about liveview and after it was all done, I was almost ready to delete it. But then I showed someone and they said that it might be useful, so I posted it.
It may seem weird, but I don’t talk about projects I’m working on because it reduces focus. And I don’t talk about projects I’ve finished because they are already done. And this is why I may be terrible at marketing and self-promotion.