Brought to you by
Workshop logo

From Rails to Elixir & Phoenix Workshop

Learn how to build web apps of a new era with simplabs and Mike North.

Sign up to be notified about future dates and locations

  • [...] There was a very broad consensus that your style and depth would be a great fit for our team. John Norman, Chief Architect Iora Health

Phoenix and Elixir

The Phoenix Framework allows developers to build robust, performant and scalable web applications faster than ever. By combining Elixir's modern language syntax and tried-and tested foundation, with many of the productivity benefits that made Ruby on Rails astonishingly popular, this new web framework promises to be great (dare I say, ideal?) option for a variety of projects.

The Workshop

In this workshop we'll begin by teaching you a little bit about Elixir - an easy-to-use functional programming language that runs on the BEAM (Erlang's virtual machine). We'll then dive into our first Phoenix app, tackling topics like data persistence, routing, authentication and error handling before the end of the first day.

On day two, we'll go back to learning about pure Elixir, tackling some more advanced topics like inter-process communication, distributed storage with ETS and security. After lunch, we'll put some of this knowledge to work in our Phoenix app by writing some tests, hashing passwords, encrypting database columns and more!

Day 1

The First Sip

Before we jump right into the framework, we need to at least know the basics of the programming language we're working with.



Welcome & Getting Started

We'll go over our agenda, and set our sights on some goals for the day.


Origins, Foundations & Core Principles

Elixir is unique, in that it provides us the fantastic ergonomics of a modern programming language, while standing on the solid and battle-tested foundation of the Erlang ecosystem.


Interactive Elixir

Elixir's interactive shell (IEx) is one of the most powerful tools in your toolbox. We'll outline some of the most useful features for beginners, including

  • Running scripts
  • Getting metadata about a value
  • Accessing embedded documentation
  • Inspecting the state of a particular process


IO & Files

As with most programming languages, it's useful to know how to interact with files and humans. We'll take care of this early on, and notice a few things that foreshadow some interesting aspects of Elixir's concurrency model.

Types, Operators & Control Flow

One must crawl before one walks, and it all starts with basic types and procedural logic. Even if you're experienced in a wide range of programming languages, there's going to be a lot of stuff - even at this basic level - that may change the way you look at writing code forever.



Math & Strings

There's no getting away from these kinds of things. Eventually you're going to need to work with numbers and text, so we'll start with a crash course in some core APIs (including a dip in the erlang pool) that will make life easy.

There's a lot of capability here, but we'll stay close to the commonly-useful and pragmatic path.


Exercise: Projectile Motion

We'll create a simple program that calculates and object's projectile motion, given a launch angle and initial velocity.


Exercise: String Acrobatics

We've got a bunch of functions that do various things to strings, but our tests are failing.



It stands to reason that functions are really important in a functional programming language. We'll build and work with named an anonymous functions, combine functions together to form pipelines, and even map out some higher-order functions of our own.



Break for Lunch


Tuples and Lists

Often times we find ourselves needing to work with several objects in a "collection", and will need to choose between Elixir's List and Tuple types. We'll compare and contrast tuples and lists, and write a few programs highlighting the benefits of each.


Associative Data Structures

We have two main associative data structures in Elixir: keyword lists and maps. Let's learn more about them!


Exercise: Building up a List

Assembling a bunch of items in a list is really fast, as long as we do it in a way that doesn't involve moving existing items around in memory. We'll write two programs, one which assembles a bunch of dictionary words into a tuple, and another that uses a list instead.


Pattern Matching & Guards

This modern language feature allows destructed assignment, and is often used in order to define several variants of a function, each to handle a specific scenario. This application of pattern matching reduces what would otherwise be a lot of internal function complexity by huge amounts.


Exercise: Function Refactoring

We've got an Elixir module that involves some code that could benefit from some pattern matching magic. Refactor the monolith function so all use of if/else are replaced by creating new functions oriented toward handling that specific pattern of arguments.


Conditionals & Guards

It's unusual to use if/else in Elixir, because we have some far more powerful approaches to deciding among different branches of code to use. We'll look at the case control flow structure, where pattern matching really starts to shine. We'll also take a look at how guards can be added to case clauses (and other control flow structures) to form even more specific and targeted patterns.


Exercise: more Refactoring

We'll refactor some more code to leverage the power of pattern matching and functional control flow tools.


Wrap up

Wrap up for the day

Day 2

Writing modular Programs

Elixirs module system allows us to define layers of related functions. In this part of the course, we'll explore the concepts of modules, and the ability to reference code in one module from another.



Modules & three important Directives

Modules are just a group of several functions, some of which may be private and some of which may be public. Modules give us the ability to define named functions using the def macro, which offer a few other features that were unavailable in the world of anonymous functions.


Exercise: Mission Control

We've got a set of tests for a couple of Elixir modules that are used to control a space ship. Alter the code to make the unit tests pass, and ensure that you've kept as much of each module's internal functionality private as possible.


Basic Metaprogramming

While the use macro is not strictly a directive, it's of particular importance when considering "mixins" for common functionality across multiple modules.


Exercise: Extending a Module

The use macro can essentially be used to decorate a module with some code from another.

Working With Data Structures

Earlier we outlined and worked with several different types of data structures. Let's take a closer look at some ways



Exercise: Map, Filter, Reduce

We have a program that starts with a list of objects read from a file. Using the built-in functions available in the Enum and Map modules, filter out "inactive" items (objects where the "active" attribute is not true), and then log a list of object names to the console.


Taming List Enumeration with Comprehensions

Often we find ourselves looping over something enumerable, mapping values into another list, and potentially filtering out some unwanted items. Comprehensions use a generator and a filter to provide some excellent syntactic sugar for this kind of task.


Exercise: Comprehensions

Take another pass at the previous exercise, and use a comprehension to devise a concise solution.



Break for Lunch

Request, Response

A Phoenix app can basically be boiled down to a function that receives a HTTP request, and returns a response. We'll begin with this premise, and start to understand the important parts involved in this process.



Endpoint & Routing

Requests enter your app through an Endpoint, and your app usually will have only one. We'll look at this chain of Elixir Plugs, which ends at the Router, the module ultimately responsible for delegating request-handling to an appropriate Controller.


Plugs & Pipelines

Plugs are at the core of Phoenix, and they're a relatively simple and approachable concept: things that accept a connection as an argument, and return a slightly-modified connection.

Chain a few plugs together, and it's easy to see how basic building blocks start to assemble into a complete application.


Exercise: Routing to the Pages Controller

Add a new page to your app, following the existing example set up in your PageController.


Exercise: Hating on a Content-Type

Build a Plug that interrupts the pipeline (returning a HTTP error for an incoming request) if we ever request a SOAP XML document (Content-Type: application/soap+xml).


The Controller responds

Now that we understand how to leverage Phoenix's routing layer, let's take a closer look at Controllers: the modules ultimately responsible for responding to a request.


Views & Templates

In a welcome contrast to other web frameworks, Phoenix's view layer is exceedingly easy to understand and use.

Judging by how easy it is to keep views simple, performant, and easy to manage, It's clear that the hard-learned lessons from older frameworks have paid off.


Exercise: Assigns & Functional Views

We'll pass some data from the Phoenix controller layer to the view layer, and leverage view functions to do some light formatting & massaging.


Testing ergonomics is perhaps the most impactful factor in determining whether writing tests is an enjoyable part of day-to-day development, or an annoying slog that's neglected until problems arise.

In this area, Phoenix does not disappoint. We'll focus on several useful patterns for unit and acceptance testing, with the aim of making tests quick, easy, maintainable and intuitive.



Controller and View Tests

Sometimes we use Phoenix to render HTML, so we'll look at how we can verify that both our controller and view layers (individually) are doing their job.



Often we use Phoenix Controllers to render JSON. We'll explore some built-in helpers that are well-suited for helping us write tests verifying that the JSON contains what we expect, and touch on a few libraries that make this even easier!

Day 3

Managing Data

Data is an integral part of virtually any web application, and a great persistence library make a huge difference in performance and maintainability.

Thankfully, the Elixir ecosystem has us covered in spades. Ecto is a thin layer of functions that allow us to build composable queries, validate fields, and seamlessly transform records between our DB and application representations.



Intro to Ecto

Heavy persistence libraries like ActiveRecord offer convenience, but often become performance bottlenecks. We could make every DB query explicitly, but then we're trading in all of our ergonomics for performance.

Ecto manages to strike an enjoyable balance, where we are asked to be deliberate about the records we fetch from a database but (most of the time) aren't dragged into the world of writing SQL queries explicitly.

You'll be amazed at how much we can do with just simple functions, and will never look at other persistence frameworks quite the same way again.


Managing Migrations

If you've never used code to manage changes to your database schema, you're missing out. Migrations allow us to change our schema in (ideally) reversible steps, so we can apply and un-apply a set of changes while building features. Even if you've seen migrations before, there are some useful things to know about how they work with Ecto, and in particular, Postgres. We'll look, specifically at:

  • Postgres array and jsonb column types
  • Changing column types, while remaining backwards compatible


Cracking Changesets

This is one of my favorite parts about Ecto, and one of the parts you'll be most often working with. In contrast to other persistence libraries, the concept of the shape of a record (schema) and the logic for checking the validity of values (validations) are decoupled. There are some incredibly exciting consequences of this design decision.

Ecto ships with a bunch of validations, and because it's so quick and easy, we'll write a few of our own.


Exercise: Models & Validations

Create models and appropriate validations for a blog post/comment app.


Quick Queries

While we could use SQL syntax to retrieve records from our database, doing so would open us up to a world of pain. Ecto provides an approachable and composable way of building queries, while stopping short of doing us any "automatic favors" (i.e., N+1 queries) that so often degrade performance.


Exercise: Query Olympics

You'll be given a list of database queries for you and your classmates to make using Ecto. Each query is worth a certain number of points. Highest number of points after the exercise is done, wins!



Break for Lunch

Real Time

One of the places where Elixir and Phoenix leave the competition in the dust is support for soft real time programming. The ability to keep a lightweight Elixir process running for the duration of a user's time in our app, and holding some small amount of state, makes things far simpler for certain things than it otherwise would be.



Channel Basics

Phoenix Channels are a first class citizen in the framework, on equal footing with Controllers. It shows! You'll be amazed at how easy it is to start adding real-time features to your apps, where we push data from server to client.

Development best practices are increasingly moving in a functional and "stateless" direction, but Elixir Processes are a place where small pieces of state can be safely held and used. We'll explore how powerful this idea is, in the context of Phoenix channels.


Exercise: My first Channel

Build your first channel, so that you can notify client-side apps of new comments being posted to articles.


Managing Channel Complexity

While you may have contributed to a REST API project that had 10 endpoints (each handling 1-4 HTTP verbs), it's less likely that you have experience working with a long-lived web socket connection operating on the same scale of complexity. It's important to remember that this is API surface, and because it's often stateful instead of stateless, keeping organized is even more important.

Users & Authentication

Nearly every app we build these days requires some sort of authentication, and probably a user account to go along with it. Even if your app is an oddball and doesn't need this, user accounts provide us with a well-understood set of use cases that will serve as an excellent case study.

Let's put some of our newfound Phoenix knowledge into practice as we implement a secure user account feature set. The goal will be to reduce explicit management of authorization & authentication on a per-resource basis as much as possible.



Exercise: Registration

Creating new users will serve to highlight a few concepts at the model layer

  • Server-side validation, including writing our own validator
  • Safely handling passwords
  • Keeping slightly different changeset-generating functions organized

We'll also have an opportunity to start defining routes that require a user to be authenticated, and routes that don't.


Exercise: Login & Logout

For our purposes, we'll use a JSON Web Token (JWT) and the OAuth 2 password grant standard, as a mechanism and vehicle for authentication. You will be provided with a client-side app that will talk to our Phoenix, via JSON.

We'll validate a user's credentials in a way that's not incredibly sensitive to timing or brute force attacks, and then assemble our little piece of session state (the JWT) before encrypting it and handing it back to the client.


Exercise: Roles

We often have a concept of roles (or an equivalent concept masquerading as other flags/fields) built on top of our authentication. We'll add roles to our JWT, and design a plug that will raise an error if a user attempts to access a controller action without having the required roles.


Wrap up & Goodbye

We'll recap everything we've learned.

Your instructor Mike North

Image of Marco Mike North

Mike is an opinionated framework and functional programming junkie, instructor, frequent open source contributor and global speaker on modern web technologies. He has taught developers all over the world how to use Phoenix, including at Ericsson Headquarters in Stockholm, Sweden - the birthplace of Erlang, and in Sao Paulo Brazil - the birthplace of Elixir.