Stranded at the Edge
As an industry we’ve been very focused on frictionless and fast user interactions on the web, and rightly so. We’ve been pushing our tools to the limit for decades. Every turn of the wheel brings progress, but also its own set of weaknesses.
In the olden times (early 2000s) the state of the art was mostly servers delivering static pages. The adventurous among us pushed the envelope by using a frameset with a hidden frame to push data back and forth without whole page rerenders. My favorite story from that era was the day I had to add a do-nothing submit button to a form just to convince users that their data had been sent to the server. Those were the bad old days of incompatible browser implementations from which jQuery rescued us.
As our tools improved, we could push more and more of the interactions toward the client. More and more of the client code was being written by front-end specialists. The client part of our apps continues getting fatter and fatter and more separate from the server code that actually processes the data. This client interactivity is core to what we as application developers are striving for. Every time we make a user wait we cringe and look for solutions. Unfortunately, this model also has significant disadvantages. We now have two separate but intertwined apps with duplicated business logic that must be maintained in lock-step often in different languages by different teams. These apps talk to each other across the network like a rickety rope bridge across a chasm. We now have two sources of truth that need to be kept in sync and we have to make sure the UI matches app state.
Two recent inputs got me thinking more about how inefficient and downright cumbersome this model is. A talk by Todd Resudek titled "The Phoenix Project" at this year's Big Elixir conference got me thinking. Unfortunately it is not up yet. This article by Kent C. Dodds reinforced many of the same ideas. They both examine the downsides of our current model from different perspectives. I loved Kent's drawing of the network chasm and it inspired the theme for this post.
This state of affairs is expensive for app owners and painful for developers. Over the last couple of years our frameworks have made progress bridging this divide. Pushing from the client side, there are frameworks like React Server Components, Remix and SvelteKit. Pushing from the server side we have Rails/Hotwire and Phoenix LiveView and others. Of these choices, the one that stands out is Phoenix LiveView. It allows the developer to keep focused on the server end of the application while allowing for smooth user interactions on the client. This is different from “You don’t need Javascript” which server-side frameworks have been saying for years. It's that the javascript can be generalized in a way that makes it easier to connect client events with server actions. It also needs to come with useful hooks for integrating existing client components.
Phoenix LiveView creates a fast WebSocket based connection between the client and the server and intelligently sends html partials based on updated server state. Because Elixir, by way of the BEAM, easily handles tens of thousands of lightweight processes, each websocket connection has its own state which allows small update messages to be sent back and forth as long as the application or server isn’t restarted. Stateless one-off messages are great for larger, less frequent messages, but a stateful connection with small update messages is much more suitable when providing server support for highly interactive client scenarios.
How many times have we written a typeahead input only to have to add rate limiting and out-of-order message handling? Imagine a world where you can decorate your input with a phx-change
attribute that can send messages and return results as fast as the user can type? If your server process is highly complicated or dependent on a 3rd party api, it is trivial to reply with a pending message on each keystroke and reply with results when they come in. That’s the power of being able to keep your focus on the server while building those client interactions. It’s not that you don’t have to think about the client. It’s that keeping the client as simple as possible while providing the interactivity you need allows you to focus on the server where the complexity is going to exist anyway. Why build complexity on both sides of the divide if you don’t have to?
But you don’t have to take my word for it. In this series we are going to build a shared wysiwyg editor in Phoenix LiveView so we can see for ourselves just how simple the client can be while providing amazing experiences for our users. Until then, happy exploring!