After tweeting that his inbox couldn’t handle any more requests for preview access, Chris McCord recently made the initial release of Phoenix LiveView public. The repo soon appeared on Github’s Trending page indicating wide interest among developers.
Haught Codeworks is writing Elixir and Phoenix code and spent a little time taking this first release of LiveView for a test drive.
To state the obvious: Web applications run on the web. HTML is the original and standard language of the web so I welcome any opportunities to build real-time, interactive applications while writing and managing relatively standard HTML.
I also welcome opportunities to reduce the number of places that state, business logic and rendering are handled. Server-side html and state management keeps these concerns close to the server-handled business and domain logic. This proximity can reduce duplication and mental task switching while making code easier to reason about.
Depending on how it evolves, there could be a business-case for Elixir, Phoenix and LiveView. A reduced stack complexity could result in shorter development cycles and reduced costs to design, develop, maintain and extend applications.
I am excited about this. Read on to find out why!
Check this out. It’s a clock. A simple little clock. But it has generated a little excitement because in this demo created by @alephnaught2tog the clock updated a thousand times per second. On the server. With Elixir).
There are other demos including a couple small games here and developers are creating new demos daily on Twitter.
Here’s a colorful counter, a Kanban app, a server-side input box, and a calculator. Would you ever really want to have a server-side calculator? I know you will not want to use LiveView for animation. These demos are simply people experimenting with LiveView and small programs are the easiest way to do so.
Let’s break down this discussion into its parts: Elixir, Phoenix and LiveView.
Elixir: A functional language
Developers report that their Elixir code is: * concise * explicit * changeable * understandable
That’s great for software developers writing and changing software.
Yet as a basis for real-time web apps what really interests us about Elixir and other functional languages is that data is immutable and immutability is ideal for concurrency. Let’s take a closer look at Elixir to find out why.
The developers of Elixir describe the language as “a dynamic, functional language designed for building scalable and maintainable applications. Elixir leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems.”
The Erlang VM has been around 30+ years and was created to run telephone networks presenting an ideal use case for managing connections, handling and restoring drops, restarting on failures, and scaling to millions of concurrent connections.
It is this concurrency that allows Elixir to be massively scalable. To understand why, take a step back to Moore’s law which observed that the number of transistors (and therefore the raw computing power) on a chip doubles every 18-24 months.
In effect, software got faster every year even if it were not changed at all! Bonus for software developers!!
Now the rate of CPU improvement has slowed and software developers can no longer depend on faster CPUs as an answer for faster software. Instead, Elixir and other languages lean on concurrency to provide scalability and performance improvements.
As mentioned above, Elixir is a functional language. It turns out that Elixir’s functional language underpinnings are ideal for concurrency. In a functional language, data is immutable. The immutability of data rids developers of a whole set of concurrency-related problems. Because data cannot change, concurrency management issues such as data-locking go away or are drastically simplified and concurrency can be deployed at massive scale.
It is inevitable that developers would want to leverage that scalability when building applications for the world’s ultimate scalable platform – the Internet. Phoenix is a framework built to do just that.
Phoenix is described as “a productive web framework that does not compromise speed or maintainability. Phoenix brings back the simplicity and joy in writing modern web applications by mixing tried and true technologies with a fresh breeze of functional ideas.”
While Elixir and the Erlang VM are used extensively in embedded devices, web applications and non-web-supporting server processes, Phoenix is squarely a web-based development framework supporting web browsers and api-consumers such as mobile applications.
Phoenix web applications create server-generated HTML, generally in response to a request from the browser and follow the typical http request-response cycle, including authorization, authentication, routing and rendering.
This request/response cycle is a well-understood and supported approach to web application development. Yet, for interactions that desire a real-time experience, this cycle is slow and does not feel interactive.
Solutions to slow request/response cycles
With AJAX and asynchronous requests the user does not see a spinning wheel or browser-loading indicator, there is still a request-response cycle in use. The web server receives AJAX requests similarly to normal synchronous html requestss, and after responding there is no further communication nor state nor connection until the next request. The connection and state need to be rebuilt with each request.
With LiveView, the connection between the application and the client remains open throughout the user’s session. This real-time connection is provided via Phoenix channels.
To revisit how Phoenix leverages the strengths of Elixir to support channels:
Functional languages simplify concurrency, which enables a massive number of light-weight processes (Elixir processes, not operating system processes). Tons of Elixir processes allow each user to have their own connection, aka channels.
Having the ability to run hundreds of thousands of concurrent processes means that each user can have their own persistent, real-time connection between the server and their client (in this case the web browser).
By using connected, bi-directional, real-time connections, channels allow for millisecond response times.
LiveView leverages this ability.
Because LiveView keeps a socket open between the client session and the server, time is not spent on each state change to handle http tasks such as authentication, authorization, and re-building state.
Each user’s socket has Elixir processes that maintain and manage state. When an event is received from either the web browser or the application itself, LiveView handles the event by updating state on the process. Then the LiveView template’s render is performed using the new state and diff’d against the cached version of the template in order to deliver only the parts that changed to the browser.
With LiveView, the same html that you write for initial render is used for re-renders and updates. The same changesets you use for initial render and for non-live-view events can be used for LiveView actions. Whether being called in response to a plain http request or a live view, the data layer (such as ecto changesets) and the html layers do not care.
There are scenarios where LiveView is not a candidate. These include mobile native, compute-heavy applications and applications requiring offline use.
Mobile native applications: Applications desiring mobile native support will not benefit from LiveView. Unlike React, HTML is the only implementation target of LiveView. Note, however, that Phoenix applications can easily deliver API support for native mobile apps. Also, some developers have created native applications which are essentially wrappers around a web-view native component. In this approach, Phoenix could serve html to “native” apps’ web-view components.
Compute-heavy applications: Elixir is not designed for computationally intensive applications. Elixir’s power comes from the ability to manage thousands or millions of Elixir processes. Yet Elixir itself is not the fastest of languages. Similar to how you would not write a jet-fighter flight-control system in Ruby, don’t try to do so in Elixir either.
Like any new tool, we are evaluating LiveView but are cautious about a few aspects.
For sample applications like we have seen so far, the number of events and handlers is small and easily manageable. When the number of interactions and events grow to those found in a real production application, it will become important and challenging to arrange them in a way that keeps code concise and maintainable.
This approach of complete reuse between initial render html and re-rendered html could make for some sloppy UX. When UX updates and transitions are not explicitly designed and coded but instead happen automatically, less attention could be paid to handling conditions such as failed server responses or some edge cases. Sometimes out-of-sight means out-of-mind. While LiveView could easily make UX updates in response to state changes out-of-sight, we must be careful not to forget about making the experience a positive one for users.
While proximity of domain-logic and presentation in the codebase can reduce costs and complexity, a monolithic approach can blur the lines between the two aspects of an application and introduce unnecessary coupling that reduces the ability for future change. Interestingly, the fact that state changes are required in order to trigger a LiveView re-render means that performing operations in html is discouraged. Data loading inside a LiveView template will not work. This has the side effect of enforcing the best practice of keeping data loading and state calculations in the domain-specialized Elixir code and out of your view code.
Try it out!
LiveView is so easy to implement that I recommend giving it a try to determine if there is a fit for your projects. To get started, take a look at these resources: