Press enter or click to view image in full size
Considering Trio is an open-banking platform in its essence, we needed, among many other things, to have a secure way for our direct customers (companies) to connect their end-users’ accounts to banking institutions.
We also wanted something fast, given this whole experience should be ultra lightweight and compatible with all kinds of front-end, from legacy PHP pages to mobile applications.
That’s why we didn’t want, from the very beginning, to use JS-heavy frameworks like Angular or React SPA’s, either.
At last, the worst option for our case would be to create pure out-of-the-box MVC pages with jQuery and window JavaScript, given how easy it is to interact with JavaScript that has been put directly on window. It’s just bad. It exposes too many details of the internal operation and requests. Besides, just a single back-end misstep is enough to let bad intentioned clients do whatever they want with rewritten JavaScript methods.
The Idea
So we came up with either Phoenix Live View or Hotwire. Our back-end was already built upon Elixir and Phoenix, so we picked the former.
Phoenix Live View seems complicated, although in practice the concept is quite simple: it is a process that receives messages/events and updates its state according to Elixir back-end logic. The page loads first as a common HTML page, then a socket connects to the server via page JavaScript — which is practically the only piece of JavaScript we have, most of the times — and from then on, that socket can alter the page DOM whenever the back-end feels like there’s a state difference among socket’s properties.
By initially loading as a common HTML page, it will be able to load even if your client does not have JavaScript enabled, despite not changing state dynamically as it would with JS.
Also, you don’t really have to worry about rendering optimization because the framework does all the job of re-rendering changes in an effective, transparent and lightweight manner. You only need to deal with setting a first meaningful socket state and its validations through the mount function, and then control the changes via handle_event functions on your back-end.
Nonetheless, after implementing it, we’ve discovered many other benefits that we didn’t realize were there at first. Below are the main reasons that made us stick to this approach:
Press enter or click to view image in full size
Benefits we’ve found
It’s important to notice that all of this testing apparatus is already available out of the box when you have a Phoenix Live View application. You don’t have to go after external libraries or frameworks.
1. It is damn fast
As foretold, the fact that all state changing decisions are made on the server side makes it super fast and light for the client. But we didn’t expect it would be so fast on the server side as well. Even though many interactions trigger a LOT of back-end side events and even if many clients interact with them at the same time, Elixir always keeps up with the demand, without stuttering or increased response times.
2. It is easier to test and maintain
Given all state changes are in fact back-end transformations in sockets’ attributes, you can always test interactions the same way you would normally do to your conventional Elixir code: by testing its functions.
You can either:
- Write unit tests involving mount and handle_event functions, in which you provide a certain socket in a certain state, and expect, given certain params or not, that the socket leaves in a specific modified state.
- Write end-to-end tests where you simulate connected and disconnected socket states, page DOM contents, phx-events interactions (the ones responsible for pushing events from the client to the server, which then evaluates and replies with a modified socket), and even normal or live redirects (the ones that just push the socket seamlessly to another live, instead of a whole page redirect).
It’s important to notice that all of this testing apparatus is already available out of the box when you have a Phoenix Live View application. You don’t have to go after external libraries or frameworks. You can also have it working fully integrated with test coverage tools, such as ExCoveralls, without breaking too much of a sweat.
3. It is a lot harder to tamper with
One of our most important concerns, as previously said, was to avoid letting the client mess too much with JavaScript and window variables in general, which sometimes allows evil intentioned clients to do or access what they weren’t supposed to.
By using Phoenix Live View, we made sure there is basically no piece of useful JavaScript at all. The only sort of things we really do in JavaScript is input masking and other kinds of embellishment, like animations.
Plus, socket’s variables are always private and thus inaccessible when inspecting. That doesn’t mean it is impossible to tamper with at all, though. The client can still add artificial phx-events to their DOM’s elements, but that’s about it. Considering all your meaningful events will have to validate a previous socket state, session, and/or params, they won’t be able to fake any relevant event just by adding a phx-click in a certain div and clicking it, for example. That is, unless you really wanted to do that or unless you messed really hard with your back-end logic, which should be tested.
You should also be careful when adding Hooks (JavaScript interoperability), although these are segregated to certain parts of your pages where you chose to enable them.
4. It deals with failure elegantly
It is really hard for a piece of software to just “never fail”. What Live View does is basically the following: when a back-end error happens (ie: an exception being raised, or a crash), or if the connection drops, the client will just seamlessly reconnect to the server and call mount again.
It means you can either direct them back to your flow’s start, or try to recover something from the session, depending on what you do in your mount function.
In other words, the framework will automatically just put it back to work. Although, if you want, you can create your custom “edgier error treatment” by rescuing within your code.
Press enter or click to view image in full size
The Cons
Press enter or click to view image in full size
1. You have to learn it first
As with many things in life, you can’t just learn how to use it in an hour or so. Chances are, if you’re already well acquainted with Elixir and Phoenix, you might learn how to create a useful and simple Live View application in less than 6 hours.
But if you don’t know Phoenix or Elixir, you’ll have to go through them (and even through functional programming first) before being able to do cool stuff.
That’s why I listed this as a downside. It’s not the hardest framework we’ve ever learned, though not the easiest.
2. It doesn’t deal with any sort of animations per se
Phoenix Live View doesn’t provide any features to deal with page transitions or animations by itself. It means if you want them, you’ll have to come up with a custom solution using either CSS/CSS transitions or JavaScript, via hooks, only.
Even when you “live redirect” a page to another live view that uses different CSS, you’ll need some tricks up your sleeve, like creating HTML views with “display: none” and, at the end of that page’s CSS, unsetting it. Or else, the page you redirected to will look glitchy and CSS-less, even if just for a split second.
3. You’re tied to Elixir + Phoenix back-end, at least for that part of your system
This one is kinda obvious, though mention-worthy. By using Phoenix Live View, you’ll have to stick to Phoenix (and thus, Elixir) for this application.
What you can — and should — do is to avoid mixing your Live View application with logic that shouldn’t necessarily be on it. This way, you can always separate apps if they’re well modularized.
Conclusion
Considering all the good and bad parts of Phoenix Live View, we heavily recommend using it especially if you already have apps using Elixir and Phoenix in your ecosystem. It brings not only security, ultra fast speed and response times, but also strong testability and maintainability, which we have been longing for regarding front-end applications, for so long.