When we set out to build Yap, our log-free, ephemeral chat application, we knew that the marquee feature was its fleeting nature. In order to really commit to this idea, we didn’t just want your last message to disappear once you sent a new one, or for your room to disappear after a day of inactivity; we wanted to avoid ever writing anything to disk, so that your Yap chats are about as ephemeral as things get in the world of computers.
This restriction is a dead end for most programming languages, especially in the world of the web, where the stateless nature of most requests requires looking up data from… somewhere. And that somewhere is almost always the disk, most often in the form of a database query.
So how did we accomplish our disk-free goals without compromising on the functionality we wanted from Yap? We turned to a programming language called Elixir.
If you’re unfamiliar with Elixir, you’re not alone.
The quick version: Elixir is a 9-year-old programming language that runs on the Erlang virtual machine. Erlang is a 34-year-old programming language originally created to run telecom systems (which it has done successfully for decades). Erlang has recently seen an upsurge in usage specifically for building fast, fault-tolerant, real-time communication systems, most famously WhatsApp. Elixir is essentially Erlang (they’re interoperable and run on the same virtual machine) with a more conventional syntax (by modern standards) and some niceties like metaprogramming (which reduces the boilerplate of Erlang).
Elixir (and Erlang) ship with a collection of tools called the Open Telecom Platform (OTP). The name draws a direct line to its origins, but OTP is useful for much more than just telephony. As a programmer, the big “wow” bit with OTP is that it allows you to quickly and easily spin up lightweight processes that hold their own internal logic and state. Within Elixir and Erlang programs, it’s common to have hundreds of thousands of processes running at any given time. This is decidedly not normal for most languages. For many it’s just not possible, and it’s awkward and verbose in the languages that can manage it.
Selfishly, I’m always on the lookout for opportunities to use Elixir at Postlight. It’s fast and flexible, functional and concurrent, and its process-oriented architecture allows for a versatility you rarely find in other languages. If I had wanted to build Yap in the same manner using Node.js, for example, I would not have been able to do it. (For a variety of very good reasons, Node.js is Postlight’s go-to for most projects, but like every language, it has limitations.)
For Yap, we use Elixir to spin up a new process dedicated to each Yap chat, and each process holds the state for that chat in memory for the lifecycle of that particular chat. The state is limited to the people who’ve joined the chat, their latest message, the chat topic, and other associated metadata. It’s the amount of data you can easily store in just a few kilobytes of memory, and transfer to the client quickly and easily. When a Yap chat has been inactive for 24 hours, the process self-destructs, and the limited state that’s been held in memory to that point goes along with it.
The right tool for the job
Given the complexity that would normally be involved in running multiple processes, delivering synchronous events over WebSocket (including syncing presence in chat, broadcasting messages and spectator counts, and updating embedded media), the code for Yap’s backend is surprisingly simple. To me, that’s the best indicator that Elixir was the perfect tool for the job.
How about the frontend?
As for Yap’s frontend, here’s a quick rundown of notable choices made by Postlight engineer Josh Pohl:
– React: Not only does Create React App make it super quick to get started, but there’s a huge number of open source software libraries that you can pull in to avoid implementing features that have been more thoroughly-tested elsewhere.
– Reach UI: For example, Reach UI is an accessibility-focused component library that offers drop-in solutions for items like modals, tooltips, and screen reader text. Their components are used wherever possible in Yap.
– TypeScript: TypeScript was particularly helpful to add types for our back-end responses, which made it easier to incorporate the response data into our client-side reducers.
– Ky: We use a few REST endpoints for creating, joining, and checking the status of a room. Ky is a wrapper around “fetch”, which removes a lot of boilerplate.
– Phoenix: The client-side Phoenix library to communicate with the back-end. Makes it easy to join rooms, and setup listeners for all our messaging events.
– color: Since a user can select a custom color for a room, we use this module to determine contrasting colors for both animations and text. Very helpful!
Intrigued yet? Check out Yap for yourself here.