This chapter covers
Phoenix is a web framework built on top of the Elixir programming language. Every day, Phoenix and Elixir applications
In this chapter, you’ll be introduced to the Elixir programming language and the Phoenix web framework. We’ll take a look at the main advantages and disadvantages a language like Elixir and a framework like Phoenix provide. By the end of the chapter, you’ll have a good overview of these technologies and why they exist, and you’ll be ready to jump into learning the details and creating your own web applications.
Phoenix is a web framework that aids in the creation and maintenance of dynamic websites, but it doesn’t attempt to copy the big players in this space, such as Ruby on Rails. Phoenix is built on top of the Elixir programming language, which is a functional language. Although Phoenix has best practices and suggestions for structuring and maintaining your applications, it isn’t as dogmatic about these suggestions as Ruby on Rails and other frameworks are, giving you the flexibility to write your applications as you see fit.
As shown in figure 1.1, the Phoenix framework is written in the Elixir language, which in turn runs on the Erlang VM (virtual machine).
Over the years, some high-profile companies have embraced Phoenix and Elixir. They have done so in varying degrees—some started with just API servers, some used them for internal tooling, and some have gone all-in and moved everything over. Companies such as Pinterest, Slack, Discord, Lonely Planet, Bleacher Report, Undead Labs (the company behind the hit video game State of Decay), and Opendoor are on this ever-growing list. The ability to dip a toe into the world of Phoenix or fully dive in is one of the great things about Elixir and Phoenix. They can handle any size of project you throw at them, and do so very cost-effectively.
Phoenix goes beyond many web frameworks as well. For example, Phoenix has the concept of channels built in. Channels allow for “soft-realtime” features to be easily added to applications. We’ll cover the topic of channels in depth in later chapters, but they allow you to have chat applications, instantly push updates to thousands of simultaneous users, and update pages without a full-page refresh. These kinds of features can be difficult to add in other languages and frameworks, but it borders on trivial for Phoenix applications—you’ll learn how to add them to your applications in a single chapter.
Let’s look at a few of the exciting things Phoenix offers. The following sections certainly don’t provide an exhaustive list of Phoenix’s benefits, but they will give you an idea of some areas in which the framework shines. We’ll cover a number of these in more detail in this book.
What can you do with real-time communication? Well, any time you want to push information to many users simultaneously, you’ll need some solution for that. For example, in a chat application where users can send chat messages to thousands of other users and receive them in return, you need to keep each client up to date when another user submits a new message. A more complex example would be an auction site that wants to provide users visiting the auction item’s page with up-to-the-second information regarding the state of the bids. You might have a workplace collaboration site where users are sharing files and even working on the same file simultaneously. Or perhaps you want to build a real-time multiplayer game server, and you need to ensure that all players have the same information at the same time. Real-time communication is necessary in many different situations and beneficial in many more.
Figure 1.2 illustrates a typical “pull” request, in which the user has to request new information from the server.
Figure 1.3 illustrates a simple situation in which bandwidth can be saved and the user experience improved by pushing data to the user instead of requiring the user to pull the data from the server.
As the world moves more and more toward mobile communication, the real-time aspects of Phoenix can be hugely beneficial.
Elixir can spawn and efficiently handle hundreds of thousands of processes without blinking an eye (we’ll go into a bit more detail later in this chapter). When creating channels for real-time communication, Phoenix spawns each channel in its own process, so that no single process can damage or take down any of the others—they’re isolated.
Using these processes and channels, Phoenix can handle hundreds of thousands of connections on a single server. Kudos to you if your web application ever has hundreds of thousands of simultaneous users wanting to communicate in real time, but that gives you an idea of the power of Phoenix and channels running within processes.
You’ll add channels in the application you’ll build over the course of this book.
There will be times during development when you’ll want to execute a long-running computation or process but won’t want to keep the user from interacting with your application. For example, a user of an e-commerce site may want to purchase a product that’s first customized and then downloaded. When the user finalizes the purchase, you don’t want to require them to wait on that page until the personalization process is complete. Instead, you’d like them to continue to browse your store or set up their profile. This is a simple scenario in which background computation can play a role. Figure 1.4 shows how a similar asynchronous request may progress.
If your application needs to do more than a small amount of computation in the background (normally asynchronously), it’s a perfect fit for a framework built on top of Elixir, like Phoenix. It’s trivial to kick off asynchronous background processes with Elixir.
If you look at any hosting provider’s offerings, you’ll notice that adding more CPU power or cores usually costs less than adding more RAM. This fits perfectly with the way Elixir works. Elixir automatically utilizes all the CPU cores available to it and uses a relatively small amount of RAM in order to run. Compare this to a web framework and language like Ruby on Rails, which is RAM-hungry.
Other web frameworks built on different programming languages can scale, but if you’re looking for a low-cost way to scale quickly, Elixir and Phoenix might be a good choice. Pinterest explains in a blog post that they moved one of their systems from Java to Elixir (http://mng.bz/6jMA):
So, we like Elixir and have seen some pretty big wins with it. The system that manages rate limits for both the Pinterest API and Ads API is built in Elixir. Its 50 percent response time is around 500 microseconds with a 90 percent response time of 800 microseconds. Yes, microseconds.
We’ve also seen an improvement in code clarity. We’re converting our notifications system from Java to Elixir. The Java version used an Actor system and weighed in at around 10,000 lines of code. The new Elixir system has shrunk this to around 1000 lines. The Elixir based system is also faster and more consistent than the Java one and runs on half the number of servers.
Although the Elixir language and the Phoenix framework are awesome and are usually what I reach for when starting new projects, they’re not always the best choice for the job. Some areas in which Elixir doesn’t do as good a job as the alternatives include the following:
Phoenix provides a lot of things that help you add normally complex features to your web applications, but it won’t be the foundation of your application. It may be strange to read that in a book specifically about Phoenix, but the truth is that Phoenix derives its powers from the amazing Elixir programming language.
Phoenix is a framework built on top of Elixir—so what is Elixir? Here’s a quote from the Elixir homepage (http://elixir-lang.org):
Elixir is 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, while also being successfully used in web development and the embedded software domain.
Erlang has a long and interesting history. It was developed at Ericsson (the telecommunications company) and first appeared in the world around 1986. Ericsson used it internally for years to handle telecommunication jobs (such as powering telephone exchanges) around the world, but it became open source in 1998. Erlang is known to be a good fit for applications that must be fault-tolerant, distributable, highly available, and performant. One of the exciting things about Elixir is that it’s built on top of this 30-year old VM, and it utilizes those decades of knowledge and experience and marries them with nice syntax and metaprogramming support (among other things).
Let’s look at some more things that make Elixir an exciting language to work with.
One of the amazing things about Elixir is its scalability. Elixir has the idea of processes running on your machine. These aren’t processes as you may have thought of them in the past—they aren’t threads. Elixir processes are extremely lightweight (the default initial size is 233 words or 0.5 KB) and are independent of all other running processes. You’ll build a Phoenix application through the course of this book, and you shouldn’t find it surprising that a nontrivial number of concurrent processes will be running on your machine to allow the application to run. These processes are fast, are independent of each other, have unshared memory, and can easily send messages to and receive them from other processes on distributed nodes (different machines potentially in different parts of the world).
Elixir applications (including Phoenix apps) have supervisors and workers. Supervisors monitor the workers and ensure that if they go down for one reason or another, they’re started right back up. This provides an amazing backbone of fault-tolerance without much work on your part. You can configure how you’d like your supervisors to handle their workers if something does happen to them, including shutting down and restarting any sibling workers. Beyond that, supervisors can supervise other supervisors! The supervision tree can get pretty large and complex, but this complexity doesn’t necessarily mean your application will be harder to comprehend.
Figure 1.5 shows a scenario in which a supervisor is in charge of rooms for scheduling purposes. Among those rooms, there’s another supervisor that’s in charge of a subset of rooms. The top-level supervisor can certainly supervise the supervisor.
Figure 1.6 illustrates a real-life application developed by a friend of mine. Each dot is either a supervisor, a process being supervised, or both, which should give you an idea of how many processes can be spawned during the running of a moderately complex application. This application is constantly running in the background to find scheduling conflicts for an organization’s physical assets, such as rooms, buildings, projectors, chairs, and the like. Each supervisor handles a group of rooms. When a request comes in, each supervisor gets a request to ask each of the rooms it supervises whether there are any scheduling conflicts. If any of the child processes has any trouble and crashes, the supervisor notices and starts it right back up.
The benefits of these processes and supervisors are many. One, in particular, is that creating distributed systems is much easier than is possible in other languages. You can deploy your code to multiple servers in multiple locations around the world (or in the same room) and have them communicate with each other. Need more power? You can bring up another server node, and your previous nodes can communicate with the new node with minimal configuration, passing off some of the required work as they see fit.
The preceding features are available to us because the backbone of Elixir is Erlang. Erlang has been around for decades now, silently and dependently providing services to many of the things you use on a daily basis. Erlang was created with the telecommunications industry in mind, and what does a telecommunication service require? High fault-tolerance, distributability, live-updating of software—things that a modern-day web application should also be able to rely on. Elixir runs on top of the Erlang VM, which is rock-solid and has been for decades. You can even run Erlang functions and use Erlang modules directly in your Elixir applications! This book won’t go into the details of Erlang and its VM, but there are plenty of resources available that cover Erlang.
There’s a great story about Erlang and the popular messaging app WhatsApp, which was acquired by Facebook for $19 billion in 2014. When it was acquired, it had 450 million users sending over 70 million Erlang messages per second. If WhatsApp had been built on a programming language other than Erlang, it likely would have had a huge engineering staff. But because the application was written in Erlang, and Erlang provides so many benefits for real-time messaging applications, the engineering team at the time of acquisition consisted of only 35 people. Your application may not ever have 450 million users sending 70 million messages per second, but why not utilize technology that would allow you to easily do so if required?
Although Elixir runs on the Erlang VM, it is itself written in Elixir. How is that possible? This is another of the great things about the Elixir language—macros. Elixir is extensible via built-in support for macros, which allow anyone to define their own language features. You can build your own little language or DSL within Elixir itself. As figure 1.7 illustrates, 90% of the Elixir codebase is itself Elixir—the rest is made up of Erlang and various shell files.
One feature of Elixir that can’t go unmentioned is its ability to utilize OTP. OTP stands for Open Telecom Platform, but that name isn’t very relevant anymore, so you’ll almost always just see OTP.
OTP is a set of functions, modules, and standards that make using things such as supervisors and workers—and the fault-tolerance that goes along with them—possible. OTP includes concepts such as servers, concurrency, distributed computation, load balancing, and worker pools, to name just a few. It’s been lightheartedly said that any complex problem you’re attempting to solve has likely already been solved in OTP.
If half of Erlang’s greatness comes from its concurrency and distribution and the other half comes from its error handling capabilities, then the OTP framework is the third half of it.
Frederic Trottier-Hebert, “What is OTP?” in the tutorial Learn You Some Erlang for Great Good!
With all that greatness backing Phoenix, what if you don’t know Elixir? That’s OK. Elixir is a relative newcomer in the world of programming languages. Chapter 2 will take you through the basics of the Elixir language, and you can refer to appendix B for some extra resources you can check out. As a teaser, the next section is a very brief tour of what Elixir looks like and how it functions.
First and foremost, Elixir is a functional programming language. A few of the biggest bullet points of the benefits of FP include
As a result, applications developed in functional languages are relatively easy to reason about, predict, and test.
Ruby, Python, Java, and C# are all OO languages and are widely used in web application development. In recent releases, even historically procedural languages like PHP and Perl have been gaining OO features. And although the number of developers productively using these languages remains high, there seems to be a recent shift away from the notion that OOP is the best way forward, and toward more FP languages (such as Elixir, Haskell, Lisp, Clojure, and so on). Even though most OOP languages can be applied in a functional way, having a language that’s strictly functional from the beginning has its advantages.
Functional programming is all about data transformation. Mutable (changeable) data and changing state are avoided (and in some cases are impossible). A function that’s called with the same parameters any number of times will return the same answer every time. In purely functional code, no data outside of that function will be considered when the return value is computed. One of the results of this is that the return values of functions need to be captured in order to be used again—there’s no object involved that encapsulates its own state.
Let’s suppose you want to do some calculations regarding a race car and keep track of things like what kind of tires it has, its speed, its acceleration, and so on. You could group that information into an Elixir module that would not only define the structure of the data but also be a central place to hold all the race car functions.
Let’s take a look at what a race car module might look like in Elixir. Don’t worry too much about the syntax—we’ll cover that later. The following listing should give you an idea of the code syntax and structure.
defmodule RaceCar do defstruct [:tires, :power, :acceleration, :speed] 1 def accelerate(%RaceCar{speed: speed, acceleration: acceleration} = racecar) do Map.put(racecar, :speed, speed + acceleration) 2 end end ferrari_tires = [ 3 %Tire{location: :front_right, kind: :racing}, 3 %Tire{location: :front_left, kind: :racing}, 3 %Tire{location: :back_right, kind: :racing}, 3 %Tire{location: :back_left, kind: :racing} 3 ] 3 ferrari = %RaceCar{tires: ferrari_tires, 4 power: %Engine{model: "FR223"}, acceleration: 60, speed: 0} ferrari.speed # => 0 RaceCar.accelerate(ferrari) 5 # => 60 ferrari.speed 6 # => 0 new_ferrari = RaceCar.accelerate(ferrari) 7 new_ferrari.speed # => 60 ferrari.speed # => 0
Look carefully—the ferrari variable isn’t changed when you pass it to the RaceCar.accelerate/1 function.[1] You could run that line 1,000 times and you’d get the same return value every time: an updated structure of the ferrari with a new speed. But remember, the original ferrari doesn’t change in memory. You have to capture that return value in order to use it later.
The RaceCar.accelerate/1 syntax means the accelerate function inside the RaceCar module that has argument arity (the number of function arguments) of 1.
What this kind of programming offers is the elimination of side effects. You can run your function at any time and be confident that it will always return the same value for the same input—regardless of the time of day, global state, what order the functions were called in, and so on.
Eliminating side effects, i.e., changes in state that do not depend on the function inputs, can make it much easier to understand and predict the behavior of a program, which is one of the key motivations for the development of functional programming.
Wikipedia “Functional programming”
In simple terms, OOP involves objects holding onto their own state (data) and providing methods that allow the outside world to access, modify, or even delete that data. In contrast, Elixir modules don’t store state (data) on their own—they simply provide functions that operate on the data passed to them and return the resulting data to the caller.
Figure 1.8 illustrates how objects handle data in OOP, and figure 1.9 illustrates how FP paradigms handle data. In the latter case, the data does not belong to the module.
In the world of OOP, an object’s internal data or state can be mutated by many things during an operation. Because of the often dynamic nature of OO languages, a third-party package could hook into your codebase and modify your data without you even knowing. In contrast, when working with Phoenix, the data flows in a single line from the user making the request to the requested page being displayed. And for each step the data takes, you specify how that data is passed along. The side effects are therefore minimal (and usually are eliminated entirely).
This chapter has provided a brief overview of what Phoenix and Elixir are and some of the benefits they provide to developers. Hopefully, it has also piqued your interest in the language and framework. In the next chapter, you’ll be introduced to the Elixir language, in case you’re not already familiar with it. Then, starting in chapter 3, we’ll dive into Phoenix. By the end of the book, you’ll have written a very basic auction site (like eBay) with real-time bidding. Let’s get going!
3.14.70.203