Hi, please introduce yourself.
Hi, I’m Sophie DeBenedetto, and I am the co-author, alongside Bruce Tate, of the Programming Phoenix LiveView book.
Would you share a little bit about yourself?
Currently, I’m a staff engineer at GitHub, where—to head off any questions on this topic that people tend to pick up—we actually don’t use Elixir. I wrote Elixir professionally in my previous role where I was working on the engineering team at the Flatiron School.
That’s where I actually was first introduced to Elixir by a colleague of mine. I was part of the effort there to adopt Elixir within that organization and I fell in love with it during that process. It was such a great team, everybody was so excited about learning. We were able to be so productive and build so many cool things that solved really hard problems in a way that was fun and easy using Elixir. That was my intro into Elixir and into the Elixir community.
I’m also the co-host of the Beam Radio Podcast together with Bruce Tate, who is the co-author of Programming LiveView, and a couple of other really great people, including Steven Nuñez, my colleague back at Flatiron, who first introduced me to Elixir. I also host that with Lars Wikman and Alex Koutmos, and we are very excited to be welcoming a new co-host into this rotating group of podcast hosts—another former colleague and good friend of mine, Meryl Dakin.
Hmmm. What other Elixir things do I do?
I’m on the board of the Erlang Ecosystem Foundation. Erlang Ecosystem Foundation is a nonprofit group that supports the growth and development of the Elixir, Erlang, and broader BEAM community. I definitely encourage listeners to check that out, see what our various working groups are engaged in, and if there’s a way for them to plug in, I really encourage folks to do so.
I also do some Elixir mentoring when I can here and there. Bruce, together with his wife Maggie, runs a mentoring group for folks who are underrepresented in tech who are learning to program Elixir. I try to participate in that when I can—it’s really rewarding and I have a great time.
The Elixir ecosystem is made up of so many components. Can you give just a general overview and explain where LiveView lives?
That’s a great question for folks who are trying to orient themselves within the Elixir ecosystem or the BEAM community and just trying to get a handle on things. Like you said, what is LiveView? When would you reach for it? What is it good for?
Elixir is a language that runs on the BEAM, which is the Erlang VM—the Erlang Virtual Machine. It’s built on top of Erlang. If you’re coming from an OO background or if you’re coming from a Ruby background like I am, I try to sometimes think of it as Elixir is to Erlang what Ruby is to C.
Elixir, because it’s built on top of the BEAM, gets to take advantage of the Open Telecom Platform. OTP is a nonsensical acronym at this point. What it really refers to is a suite of libraries that allows us to write code that can be concurrent and fault tolerant at scale.
OTP is what really gives you the fault-tolerant capabilities of Elixir, because OTP is where we get the tools that we use to write code that manages failure and very often restarts your program when things fail. That’s what we mean when we say Elixir is very fault tolerant. It’s very resilient to failure.
Phoenix is the go-to web framework for Elixir. Phoenix has always been a great framework to pick if you’re looking to build web applications that are super interactive and responsive and really leaning into this real-time theme that we see on the web.
These days, even your most basic web pages or websites need some degree of interactivity. Users expect to see notifications appear before them on the screen. They expect to be able to see updates happen on the page quickly when they interact with that page.
“[Users] expect to be able to see updates happen on the page quickly when they interact with that page.”
Phoenix has always been great with that because it’s written in Elixir and built on top of OTP. It uses OTP, together with the WebSocket protocol, to allow you to build websites that support real-time functionality. With WebSockets, the server sends updates down to the user and their browser without the user necessarily having to request those updates.
Phoenix made it easy to work with WebSockets, but it still required a lot of boilerplate and effort on the developer side when working with Phoenix Channels. This is where LiveView comes in.
LiveView is just a wrapper around Phoenix Channels. If you’ve worked with Phoenix Channels before, you know that they allow you to build web pages that are super interactive and highly responsive. They deliver all the real-time capabilities that your users could possibly want or need. Now, with LiveView, you can deliver this same functionality by writing mostly pure Elixir code on the server side.
One of the things that I love about the LiveView framework, and this is a running theme in the book, is that it really takes care of all the hard parts for you. You never have to tell LiveView how to do something. You just have to tell it what to do, where the “what” is specific to the needs of your application.
“One of the things that I love about the LiveView framework, and this is a running theme in the book, is that it really takes care of all the hard parts for you.”
What I mean by that is that you never have to tell LiveView how to send a message from the client to the server or how the client should respond to a particular message by updating a certain portion of the webpage. All of that is handled by the framework. All you do is tell LiveView what should happen when the user clicks a particular button. We see that again and again throughout the framework. The framework takes care of the hard parts and you can really just focus on writing code that builds the features of your specific application.
You talk about specifying what it is you want the user experience to be. How much of that is inherited from the declarative nature of Prolog, which fed into the development of Erlang?
I think the mindset is really a functional mindset and it’s a declarative mindset. Elixir is a functional language, and it’s also a language that we take a very declarative approach to. That’s absolutely the theme that runs through LiveView as a framework. LiveView handles the hard parts, the tedious details that instruct the server and the client—for example, how to communicate between one another.
I think a good example is, let’s say a form, a user filling out a form and then they submit the form and they expect to see something has changed on the page. Maybe the form is gone and instead they see the results of having submitted that form. Maybe there’s a success message.
With LiveView, the framework handles all of that for you. All you have to do is say when the client or the user submits this form, this is the message that gets sent to the server and this is what the back end would do to save that form. All those plumbing bits are taken care of for you. It’s a theme that we see in LiveView, and then it trickles all the way down through Phoenix. You see it in Phoenix and you see it in Elixir as a language as well.
“All those plumbing bits are taken care of for you. It’s a theme that we see in LiveView, and then it trickles all the way down through Phoenix.”
I think the attitude of the LiveView framework maintainers and the Elixir language maintainers is very much inspired by the declarative mindset and of course the functional mindset. It’s that we should expose an API to our users that is a pleasure to write, that is easy to use, and we should let the language, if appropriate, or the framework, if appropriate, handle the imperative details.
You mentioned Ruby and Rails during your answer. How does Phoenix and Phoenix LiveView compare to them? Do they fill the same roles, or is there a different scope for these tools?
That’s a great question. I’m going to try to answer it without saying anything negative about Rails, because I work with Rails and use Rails to solve lots of problems today. To pick back up on that question of how do Phoenix and LiveView as a framework compare to Rails, I think they scratch a similar itch for sure.
Rails historically has been the web framework that teams and organizations will reach for to build most common standard web applications to fit the needs of their organizations and users. That’s exactly what Phoenix is designed to do as well. Phoenix in general, though, and LiveView specifically, put the real-time needs of the modern web application first.
“Phoenix in general, and LiveView specifically, put the real-time needs of the modern web application first.”
I’m just going to go out and say that I think that Phoenix is a better version of Rails. I don’t think there’s anything that you can do in Rails that Phoenix is ill-suited for, and I think that there are things that you can do with LiveView that Rails absolutely cannot match, and that has to do with the interactive and real-time nature of building LiveView web apps.
In Rails applications, the real-time functionality, the interactive functionality is there for you. I forget the web socket framework that’s part of Rails now. It might be called Hotwire, but I’m not totally sure. I don’t really think it can match the capabilities of LiveView, because it’s missing the BEAM.
It doesn’t have the concurrency and the fault-tolerant capabilities that LiveView brings to the table, because of the way that LiveView is built with Elixir on top of OTP. You don’t have fault tolerance in the same way, you don’t have scalability in the same way, and it’s just therefore not as elegant a framework as LiveView.
“Then, I think to add to that, LiveView gives you the tools that you need or the levers that you need to pull as an application developer to add pretty much any piece of interactive or real-time capability that you can imagine.”
How ready is Phoenix and LiveView for production use?
Phoenix is stable. It’s definitely something that folks have been using in production for many years now, and there are a number of companies large and small that rely on Phoenix to deliver their products to their customers.
LiveView is not yet at 1.0. We’re all watching and waiting for it to hit 1.0. It has undergone a lot of changes in the past couple of months. The past couple of releases, combined with this latest LiveView release, have seen some pretty major and exciting changes to how we write LiveView code, but the basic functionality of LiveView as a framework and the basic behavior of LiveView as a framework, I would say, is very stable and has not changed almost since the outset.
As for whether people should reach for it today and then expect in six months to say, “Oh my gosh, I have to rewrite all my LiveView because it’s changed so much,” I don’t think it’s going to change so drastically that I wouldn’t recommend people use it today. I definitely recommend and I want to see people reaching for it today, and I think you can always catch up with the upgrades on your own time on your own timeline.
“If anything, [LiveView is] definitely getting better and better.”
HTML provides text entry fields, buttons, and other form elements. Then there’s CSS, which adds style and how items will render for the user. Where does LiveView fit into this? Does it provide user interaction libraries? Does it provide look and feel? Or is it primarily concerned with the transmission of user intent back and forth between the server and the webpage?
When LiveView first came out, it was definitely primarily concerned with the latter, with building a framework that was responsible for handling those interactions between the client and the server. You, as a LiveView developer, were still writing primarily HTML templates and adding in lots of CSS and responsible for styling that code and making things look how you wanted. That has changed in recent releases, and it has especially changed with the latest Phoenix release, which bakes the Tailwind CSS framework into Phoenix.
In addition, now when you generate a new Phoenix LiveView app, you get this module called CoreComponents that offers you a set of LiveView function components that wrap up pretty much all of the common HTML building blocks and add all of the default Tailwind CSS styles to them. What Phoenix LiveView really offers you today with the latest release is not only a framework that handles the client and server real-time communication for you, but also an eloquent and accessible DSL for composing your UI—allowing you to do things like build a table, build a list, add a header, and get those default CSS styles in there for free.
What are the primary responsibilities for LiveView? Where’s the cutoff between LiveView and Phoenix?
It’s a hard question to answer because they’re really embedded in one another at this point. I would say that if you are building a Phoenix web app today, there is no reason for it not to be a LiveView-enabled application. You might have some static views that are not backed by LiveView, and you may have some views that are backed by LiveView, and then you can use the LiveView component HTML building blocks that I was referencing earlier throughout your application.
LiveView as a framework is very clearly responsible for that interactive communication over web sockets between the client and the server, but really, at this point, Phoenix and LiveView fit together so seamlessly to offer you as a web developer this suite of tools for building single-page reactive web applications that I’m not even interested in trying to pick apart exactly where to draw that line.
When working with Phoenix LiveView, are all the details about concurrency and scale and distribution and fault tolerance, are those basically excluded from your concerns because of the way this new tool has been added, or is it something you have to bring into your code and your development?
You get all of those features for free when you’re working with Phoenix LiveView. I think it’s interesting that an argument that I’ve heard from folks who are sometimes hesitant to adopt even just plain Phoenix over the years will say, “Okay, it’s great that it’s highly concurrent and really fault tolerant, but I don’t really need to use those features in my application code.”
It’s true that you don’t necessarily need to write code that takes advantage of those OTP features. But what a lot of people don’t realize is that you get those for free every time you spin up a Phoenix web server, which uses Poolboy and Cowboy libraries to make sure that the way that your web application handles incoming web requests is highly concurrent, and it’s highly fault tolerant.
Baked into sort of the very skeleton of Phoenix itself are some of those pieces of OTP functionality that make Elixir so appealing and make the BEAM, I think, so appealing as a runtime. And again, you get all of those for free with LiveView.
Every live view in your application is really nothing more than an Elixir process under the hood, but you don’t have to concern yourself with any of it. If you are writing LiveView code, you’re writing code that is fault tolerant, that scales incredibly well, that can be highly concurrent.
Then you don’t have to worry about any of it; you just get that for free. But because the OTP building blocks are accessible to you and because, like I said, LiveView is nothing more than a process, it’s really straightforward for you to build even more functionality on top of the OTP functionality that LiveView comes with.
For example, one of my favorite features to build into LiveView applications are these distributed real-time features, which is to say that if user A is looking at the browser and maybe does some interaction like clicking a button, user B, C, D, anybody else around the world who was looking at that same page could see their same page in the browser update with the results.
You can think of a chatting application as a really good example. Collaborative/multi-player games are another good example of one person taking an action that needs to be reflected on everybody else’s webpage.
It’s really simple to build those kinds of features in LiveView because each live view is nothing more than a process. Every Phoenix application comes with a PubSub server baked in, and that server will start up for you for free when you start up that Phoenix web server.
All you have to do is tell your live view process to subscribe to a particular topic, and then when user A clicks a button, makes a move, sends a message, whatever, you broadcast a message and you teach your other live views how to respond to that message.
We can use the tools of OTP, we can use PubSub, we can use message passing between processes to even further extend the real-time interactive functionality to make it distributed in our LiveView applications if you want to. But you don’t have to, your live views are still fault tolerant and scalable to an incredible degree because they’re built on top of OTP for you.
What does LiveView code look like? What are you doing with the Elixir language to turn something into LiveView?
LiveView code is just Elixir code. There’s nothing, I think, too special or intimidating about it. I can take you through the very most basic process of spinning up a new LiveView. The first thing you would do is define a route in your application router. This time you define a very special route called a live route, instead of using one of the HTTP methods like “get, “post,” et cetera, you use the “live” method to define your route.
Then you define a LiveView module that corresponds to that route, and you implement the LiveView callback functions for setting up the internal state of the live view and rendering that state into a template. Then you’ve got a working live view.
You could spin up an interactive LiveView web page in around five minutes if you have a Phoenix application, and you could probably do it with something like 20 or 30 lines of code depending on how much HTML you’re wanting to display to the user.
I think, for that reason, even if you’re new to Phoenix or new to Elixir, I think LiveView is a pretty accessible framework, and I’m excited to see people starting to adopt it and learn with it and play around with it even when they are at that more beginner level. I don’t think being a Phoenix expert or an Elixir expert are prerequisites at all for getting your hands dirty with LiveView.
How do you ensure that there is a decomposition of responsibility, that there’s a development in layers where the semantics of your web application are separate from, for example, the view of your application?
When LiveView first came out, that was a much harder problem to solve. But in recent years, and especially with the newer releases of LiveView, there are, I think, some very specific patterns that LiveView exposes to keep your code clean and organized.
I think there are two things that you can use to write LiveView code that is composable, that is semantic, and that’s maintainable and clean. The first tool that you can apply here isn’t even specific to LiveView, but it’s a pattern that Bruce Tate actually came up with in his previous book, and we call it the CRC pattern–or Construct Reduce Convert.
It’s this idea that we have some functions that are responsible for constructing or setting up the initial state of some data. We have some functions that are super-small, single-purpose, what we call reducer functions that take in that data and make one tiny change to it.
Then we have these converter functions that take that set of data and convert it into something that the user can see or interact with. Individual live views fall so nicely into this pattern.
We conceive of our mount function as a constructor function. That’s the place where you set up the initial state of the data that you want to display on your webpage. We have these single-purpose reducer functions that help us set up that state or that we can use in the event handlers that you would implement to respond to user interactions. Then, you have the LiveView render function that allows you to render the HTML templates that display that data.
If you keep that CRC pattern in mind and use it as a framework to understand what the different callbacks and the LiveView lifecycle do, I think it lets you write really clean code in your individual live views.
“If you keep [a Construct Reduce Convert] pattern in mind and use it as a framework to understand what the different callbacks and the LiveView lifecycle do, I think it lets you write really clean code in your individual live views.”
Beyond that though, your individual LiveView pages, the functionality that you’re building, can certainly become complex because we want to offer our users the capability to do lots of different things and support lots of different interactions on a page. But LiveView still, I think, makes it easy for you to find a sane and organized home for all of that complexity in code with the help of components.
There are two types of components in LiveView. There are function components, which you can also think of as stateless components, and then there are live components, which you can think of as stateful components. Function components are really just there to wrap up bits of reusable markup, like bits of HTML, for example, that you might want to display again and again, and you now get a lot of these function components for free when you generate a new LiveView app because they are included in the CoreComponents module.
You get table, list, header, et cetera, all these components that you can just call on again and again as the building blocks of your templates to structure that HTML code, to use it to render different types of data. The advantage there is that you, A, don’t have to write a lot of complex HTML, and B, those function components come with the Tailwind CSS styles baked in, so you get that advantage as well.
Then you have live components that will also render some markup and may even call on function components to do so, but they can also maintain their own internal state. You might build a separate live component to manage something like a particular form, and then that’s where you would put the event handlers for responding to the form validation and submission events.
What you’re going to find yourself doing when you build out these LiveView pages is you’re going to compose these interactive UIs out of a combination of live views, function, and live components.
A common pattern you’re going to see is that you’re going to have the parent LiveView that renders the page. That parent LiveView will probably in turn render some child function and/or live components that may in turn render some additional components. You can still follow the single responsibility principle—a single live or function component to display a particular thing or to handle a particular interaction, and you can compose them all together to render these beautiful, layered interactive UIs and still find yourself with code that I think is really manageable.
When dealing with web pages, you have the user who’s scrolling or clicking or entering data or manipulating things, but you also often have a back end. I believe that in the Elixir ecosystem that would be like Ecto or Pento or so forth. You have your state coming in and also going out in both directions. How does Phoenix and LiveView coordinate this, so that it becomes a seamless experience for maintaining state at all ends regardless of where the events are being created?
I think this is where another pattern that is not even necessarily unique to LiveView, but you’ll see throughout Phoenix, comes into play, and it’s the idea of separating your code in your web application into a core layer and a boundary layer.
The core layer in your Phoenix web application is generally the layer that interacts most closely with the database and that represents database entities. That’s where you would put your migrations, certainly that’s where you would put the modules that implement your schemas, where you define certain structs that map to database table columns. That’s also where you define your changesets for those schemas. The changeset is an Ecto construct that helps us model and manage changes to data in Elixir. That’s the core of your application.
The boundary of your application is usually represented by a module called the context in Phoenix. The context is where you write code that expects to receive input from the real world.
In a super basic example, let’s say you have a page where users can sign up for your application. You’ve got a form that the user’s going to fill out with their details. The form is going to get submitted, LiveView is going to receive that submission. Your LiveView is going to take that data and use it to invoke a function that is defined in a context module. Let’s call it the users context or perhaps the accounts context.
Under the hood of that context function, that input is going to be molded into a changeset, for example, with the help of code that’s defined in your core. Then the context function will call some code that actually executes a database transaction and tries to save that user data. Then, depending on the result, was it a successful save or was it a failed attempt to save some data? It’ll return something that LiveView can then use to determine what to show back to the user.
That’s how you string together the three layers of your application. You’ve got LiveView that’s receiving that user input, calling on context functions with that user input, leveraging core functions under the hood, and then giving some response back to the live view that called it so that LiveView can decide what it wants to show on the page accordingly.
A good rule of thumb to use when you’re trying to figure out whether code should go in your core or your boundary is: Is that code certain? Is it purely functional? Will it behave the same way every time with a certain input?
We put that certain code, or “pure functional” code in the core layer and we put code that is uncertain, code where we don’t know exactly what the output will be, in the context layer. For example, calls to actually execute database transactions go in the context, because you never know how that database transaction is going to shake out; it depends on what the input is that’s given, whether it’s valid or not.
How do things fail? How do they fail gracefully and with an expected handling?
We talk a lot about how LiveView, because it’s built on top of OTP, is resilient and fault tolerant. A LiveView under the hood is actually a GenServer. It’s just an Elixir process managed by a GenServer. If your LiveView crashes because it encounters an exception that you didn’t write code to handle, then the LiveView framework is going to just let that process terminate. Then it’s going to start that same LiveView process up again from scratch.
The user might see the page reload, which might not be the best experience if they were in the middle of doing something, but then they can interact with that reloaded page again from its original state. That’s the fault tolerance that you get entirely for free from LiveView. But again, because LiveView is a process managed by a GenServer, you can hook into GenServer lifecycle methods to handle exactly what you want to happen.
If that process does terminate, you can, let’s say, pull the state out of that process, store it somewhere such that when the LiveView starts back up, you could fetch it again and populate it so that the user doesn’t even lose track of the state of the page. That’s the beauty, I think, of LiveView being built in Elixir on top of OTP. You get a fair amount of fault tolerance for free, and you get the ability to customize it using the same levers that you can pull when working with any GenServer in Elixir.
You mentioned earlier that LiveView now vends its own base interactive controls and elements that are then styled by Tailwind. How can that be extended? How do you include customization and animation and the things that bring added value to the user experience?
You can customize the generated function components as much as you want, just know that if you’re making a change to the generated, let’s say, a table component, every time you call on a table component, it’s going to have that same change. If you need a very custom table for a specific page, it’s probably not appropriate to use the shared generated component. But there’s nothing stopping you from editing the CSS styles that are applied to any of the generated components or from adding your own custom CSS in addition to the generated CSS; you can certainly do that if you like.
How has the design of LiveView taken into account accessibility features?
That is a really important question that I feel I’m probably not the best equipped to answer, although I would like to learn more about it. I think that one of the things that I definitely want to point out is that Tailwind CSS styles are now baked into Phoenix and LiveView and make things more accessible by default. The styles that Tailwind applies are designed with accessibility principles in mind.
“Tailwind CSS styles are now baked into Phoenix and LiveView and make things more accessible by default. The styles that Tailwind applies are designed with accessibility principles in mind.”
I think that’s one real win for accessibility in Phoenix in general as well as LiveView specifically. Beyond that, I’d be curious to hear from the community what accessibility struggles people are encountering or what ways they’ve found that LiveView or Phoenix helps them meet accessibility needs and requirements.
Can you describe either the ideal organization or developer or both to adopt Phoenix LiveView or even to adopt the entire Elixir ecosystem? What makes the ideal opportunity to become part of this growing trend?
I love this question because I think everyone should adopt LiveView and everyone should adopt Elixir. But now, more seriously, I think there are definitely a couple things that would point you to reaching for these technologies.
We know that Elixir built with OTP running on the BEAM is great at concurrency and it’s great at fault tolerance. If you have a problem to solve that needs those pieces of functionality, you should absolutely reach for Elixir, which makes it easy to take advantage of those features and write code and build applications that are fault tolerant, that are highly scalable, that are super concurrent. If you’re dealing with a big-data processing problem, I think Elixir is a great framework to reach for.
“I think everyone should adopt LiveView and everyone should adopt Elixir.”
If you are a team that needs to build a web app, I have got a framework for you. I don’t see any reason why people wouldn’t reach for LiveView today. I think that it solves all of the problems that your common web app needs to solve. It allows you to build interactive, single-page web applications with ease. You get to keep the mindset of your development team firmly on the server side. You don’t have to coordinate the client and the server as separate engineering teams. You don’t have to worry about a contract between the client and the server. You don’t have to deal with the often painful and buggy release cycle of coordinating changes between the client and the server.
I have seen even small teams be very successful and highly productive when adopting Elixir and LiveView, even when those teams were brand new to Elixir, brand new to Phoenix or LiveView and new to the BEAM ecosystem more generally. I think the learning curve is really gentle. The community and the resources are there to support people and teams and organizations that are undergoing adoption journeys, and frankly, Elixir and LiveView are a pleasure to write and teams that adopt them, in my experience, honestly, have a lot of fun doing it.
“Elixir and LiveView are a pleasure to write and teams that adopt them, in my experience, honestly, have a lot of fun doing it.”
How can people follow what you’re doing, stay in touch and know what you’re going to be up to next?
I would love for folks to reach out to me on Twitter or just follow me on Twitter; that’s where I tend to just put updates with the book and other things that I’m working on. I am @SM_DeBenedetto. It’s not a clever Twitter handle, I’m not a very creative person, I guess, but that’s probably the best way to find out what I’m up to and to just say hi!