In the early days of networked computing, mainframes did all the heavy lifting: users connected to massive machines with video terminals that could do little more than send and receive text. Then in the 1970s, personal computers came along and made it possible to do serious computing on the client-side as servers handled tasks like authentication and storage in many networks. The rise of the internet in the 1990s swung the pendulum back to the server, with web browsers taking on a role not unlike terminals in the mainframe era.
The client-side made a come back over the past decade as developers built “single-page applications” (SPAs) with JavaScript. But a new crop of tools is sending the pendulum swinging back towards the server.
At the vanguard of these tools is Phoenix, a framework for the programming language Elixir, and a feature called LiveView. Using LiveView and a bit of JavaScript, developers can create browser-based interfaces for real-time applications like chat rooms or Twitter-style status updates. All UI elements are rendered on the server first and sent to the browser, ready-to-display. The only JavaScript required is a small amount of code that opens a WebSockets connection that handles sending input from the browser and receiving refreshed HTML/CSS from the server.
Phoenix isn't the first platform to offer a way for back-end developers to create front-end interfaces—Microsoft's ASP.NET Web Forms for Microsoft .NET existed back in 2002—but it did inspire many new tools. Caldera for Node.js, Livewire for the PHP framework Laravel, and StimulusReflex for Ruby on Rails, to name a few. Microsoft, meanwhile, released a new .NET feature called Blazor Server that modernizes the old Web Forms idea.
“My goal is not to get rid of single-page applications, but to obviate them for a large class of applications,” Phoenix creator Chris McCord says.
The (not so) good old days of web development
Let's say it’s 1997, and you want to make a web-based calendar app. In these early days of the web, your app would live almost entirely on the server. The server would render a static page with your appointments for each day, organized in a table of 31 boxes. You wouldn’t be able to drag and drop appointments from day to day. Instead, you’d have to include a form at the bottom of the page to add a new appointment to the calendar. Clicking the “submit” button would send the data to a server, which would store it in a database and render a new version of the page to reflect the change. In short, updating one day would require the whole page to refresh.
Even on a fast connection this wouldn't be a particularly snappy experience. But as browsers and web technologies became more sophisticated, it became possible to update only small sections of a webpage. Using a bit of JavaScript, you can create a calendar app that will display your new appointment as soon as you click “Submit”. The script sends this information to the server asynchronously, and only updates the box for the day of your new appointment, instead of the entire page. You can drag and drop appointments or even build a calendar that works offline, in case you lose your internet connection, and then syncs changes when you have connectivity.
Over the years, open source JavaScript UI libraries like Angular.js, React, and Vue.js have made it possible to create increasingly sophisticated, desktop-like app experiences on the web. This approach has many advantages, including responsiveness and offline capabilities.
But there are tradeoffs. By offloading more compute to the browser, you place more of a burden on the client's CPU and battery life. The need for clients to download JavaScript application logic can increase file sizes and eat through bandwidth. And you'll often end up with two code bases: one for the client, written in JavaScript, and one for the back-end, often in a different language.
For many applications, those trade-offs are worth it—for example calendars that need offline support or games that require a lot of client-side application logic. But Phoenix creator Chris McCord argues that for applications that rely heavily on a network connection, it makes more sense to handle application logic and page rendering on the server side.
Take a browser-based chat application, for example. Traditionally, you might build your app using JavaScript to accept user input, send it to the server, fetch text from other users, and then redraw the page. With LiveView, the browser opens a WebSockets connection with the Phoenix server and passes text to it. The server gathers all the messages from other users in the chat room, renders the part of the page that changed, and sends it to the browser. Not only does this reduce the amount of processing the client has to do, but it reduces the payload the browser receives since the application logic isn't sent down the wire.
Phoenix rises
McCord originally wanted to create a similar experience for Ruby on Rails, so he created a library called render-sync that could re-render partials on the server and send them to the browser via WebSockets. But he wasn't happy with the performance of render-sync. He wanted something faster and more responsive than Rails, which wasn't designed for real-time applications. After evaluating several other programming platforms, he landed on the Elixir language for the Erlang platform.
Erlang was created by a team at Ericsson in 1986 to power telecommunications applications. “They were dealing with distributed systems very early,” McCord says. “They weren't worried about multi-core systems yet because they didn't exist, but the architecture they created was perfect for the future of computing.”
Crucially, Erlang was engineered around the need to support massive numbers of concurrent users. “Elixir will run millions of lightweight threads,” McCord says. But what really sets Erlang apart, for McCord, is its ability to preschedule processes so that the CPU doesn't get hung up on any single thread. That way, if one user starts some sort of particularly long, CPU-intensive process, other users don't have to wait for that process to finish before they can complete their own.
While it's possible to build this sort of fault tolerance into other platforms, McCord likes that Elixir makes it so easy to handle. “You don't have to write your code in a special way,” he says. Elixir gave McCord confidence that he could create a server-side rendering system to support real-time applications with long-lived WebSocket connections between the server and the browser—not unlike a telnet or SSH connection. This enables developers create stateful applications, rather than having to “hydrate” either the client or the server with application, as the old ASP.NET WebForms system did.
McCord started building the Phoenix framework for Elixir with an eye towards eventually adding the LiveView feature. “LiveView was always the end game, but I needed to do the plumbing first,” McCord says.
Beyond Elixir
Though McCord opted to build LiveView on Elixir instead of Ruby, there are now a few different approaches to building real-time front-end interfaces in Ruby on Rails. One is StimulusReflex, created by Nate Hopkins. Another is Hotwire, a set of tools for building real-time, WebSocket-based Rails applications, developed by Basecamp for its own Hey.com email service.
Rails developers say this new crop of tools suits their needs. Matt E. Patterson, a developer at Atlas Obscura who worked with both StimulusReflex and Hotwire in a previous role, says that even though Rails has historically been able to scale to meet the demands of a wide variety of users. “I'm not going to say Ruby is the fastest language, but most of the scalability issues people see in the real world are related to running too many badly written database queries and have nothing to do with Ruby or Rails,” he says. There are plenty of applications that don't need to support tens or hundreds of thousands of simultaneous users. In these cases it might not be worth it for developers to learn not only a new programming language but also an entirely new paradigm. That's a big part of why the core ideas behind LiveView are finding their way into so many different languages and platforms. When Caleb Porzio saw McCord's LiveView demo in 2018, he was inspired to create a version for the PHP framework Laravel. “I had just left my job and I was supposed to take a break from work and coding,” he says. “But on the second day of my sabbatical, I watched Chris's presentation on LiveView. It really popped for me. I realized you could take Laravel to a whole new level.” He created the first prototype of what would eventually become Livewire for Laravel in a single day.
Porzio first tried to make Livewire work much like LiveView. “I had these long-lived processes running on the back-end using WebSockets,” Porzio says. “But PHP isn't really built for that.” He wanted something that would work well on existing PHP infrastructure without the need to create custom architectures. So he switched to using AJAX requests to pass state back and forth from browser to server. In other words, Livewire isn't technically “live” in the same way Phoenix LiveView is. “But we can fake it pretty well,” Porzio jokes.
Yes, this is slower than opening an always-on connection that maintains state. But Livewire does a few things to make this more efficient, like prefetching. And Livewire is able to avoid addressing certain pitfalls, like what to do if a user's connection breaks.
Livewire users say they're happy with those trade-offs. Tina Hammar is a full-stack developer in Stockholm, Sweden. She started out as a PHP developer. Then she branched out into building single-page applications using Laravel and the JavaScript framework Vue, which she used to build her software-as-a-service application BokaMarknad (Swedish for “book a market”) in 2019. But after Livewire was released in February 2020, she completely rebuilt BokaMarknad using Laravel and Livewire.
Previously, Hammar says she essentially had to maintain two codebases: one for the front-end, written in JavaScript, and one for the back-end, written in PHP. Livewire made it possible to consolidate the entire application into a single codebase, which makes the code more understandable. Keeping much of the application logic on the back-end also gives Hammar more confidence in the security of the application because there are fewer ways to expose credentials or other sensitive data to users.
And it lets her focus on using the language she knows best: PHP. “I love PHP and the Laravel community is great as well,” she says. “I don't mind writing JavaScript. But it's simpler to focus on PHP.”
Freelance full-stack developer Josh Hanley had a similar experience migrating an application from using both Vue and Laravel to just Laravel with Livewire. He agrees that the issue isn't JavaScript per se, but the need to maintain two separate codebases. “I can do everything in my blade templates,” he says. “That's the main attraction.”
The end of the pendulum?
Of course, many applications that use tools like LiveView, StimulusReflex, or Livewire still use JavaScript. But the JavaScript is embedded into the templates used by the back-end platform, so there's only one codebase even if an application uses multiple languages.
Several JavaScript UI libraries—including React and Vue.js—already support server-side rendering, and have since before Phoenix LiveView hit the scene. These make it possible to consolidate the application into a single JavaScript-based codebase. The question is what makes most sense for any given application.
“If you find yourself writing your HTML templates in a JavaScript framework instead of Laravel, that would be a sign that you might be better off with a single-page application,” Hanley says. “At that point you need to question what you're trying to achieve and what sorts of technologies would best achieve those goals.”
Vue creator Evan You agrees. “I don't think either model is fundamentally superior to the other, they come with different trade-offs and it really depends on the type of app that is being built and what mental model/language the developers feel they are more productive with,” You says.
Meanwhile, the line between front-end and back-end frameworks is blurring. For example, a forthcoming feature called React Server Components gives React the ability to offload more computing to the server. Today, the library’s server-side rendering simply renders a set of UI elements and sends them to the browser. From then on, application logic runs in the browser. But with Server Components, some of a React application's logic will run on the server. Conceptually, it's similar to Phoenix LiveView, but differs in that state is maintained in the client rather than on the server. This makes sense for an application that runs most of its logic in the browser.
Perhaps what we're seeing is not so much a pendulum swing, but a state of equilibrium where computing happens on both client and server in equal measure depending on the needs of the user.