Elixir Programming Language and the Phoenix Framework: What can you build with them?

You can use the Elixir programming language to build anything that you can build in any other programming language. It has a great framework for web applications called Phoenix. It can be used in embedded systems— see Nerves. It can be used for anything in between.

When people ask what Elixir can be used for, (or here) the common replies are “chat servers”, “telecomm switches”, or “APIs.” I think the reason these are the common replies is that Elixir really shines in areas where you need to handle very high volumes, have high reliability, and do real-time or near-real-time communication. And Elixir is good for those cases, but it also excels as a language for making systems that don’t need to handle huge traffic or real time communication.

Phoenix, as a framework, can essentially do everything Rails, Django, Laravel, or Spring can do. It has models (ok, schema structs), views, and controllers. In my experience, it has some serious advantages over Rails, Django, Laravel, or Spring. For one, yes, it is faster. Responses come in microseconds, not seconds or milliseconds. To me, that’s not the most important thing. What’s more important: the architects of Phoenix learned from the missteps of those other frameworks. Phoenix and Ecto (the persistence wrapper) made better choices.

To cherry pick one example: in Ecto, you have to explicitly say which related data you want fetched from the database. In ActiveRecord and Rails, if you miss a join, Rails will just load the related records when you need them. That sounds great until you put it into a loop. Rails will quietly and diligently ping your database with many queries in a row, fetching one extra record at a time. Ecto instead asks you: “Hey, did you want this? Because you didn’t ask for it.” It forces you to be clear, and in forcing you to be clear, it can be efficient.

To pick another example: in Phoenix, your entire request-to-response circuit is just a series of functions, output from one piped as input to the next. Each receives a connection and returns a connection which may or may not be different. Most of those functions sit directly in your application source code. The ones that don’t are clearly invoked from within your source code. Need to add a new junction in the chain? You go into your code and add the junction. This is how Plug works. In Rails, most of the processing of requests and responses is hidden. It lives within the Rails framework code. To modify it, you better hope that the designers of Rails left an appropriate config variable or lifecycle callback. Otherwise, you just have to “patch” Rails in memory. And you better hope that you patch the right spot and that the patch loads correctly. If the Rails team renames the methods or classes that you patched— your patch falls off and your application breaks.

So, what can you use Elixir for? What can you use Phoenix for? I personally use it for everything unless I have a good reason not to. It’s been a long time since I’ve had a reason to use something else.

Bonus Q&A: Ok, but what have you built in Elixir?

Me and my team have built, with Elixir:

  • An incentive platform for software developers
  • Tons of business workflow automation and management software for several industries
  • Real estate tools
  • Event booking software
  • Local business directories
  • Content management systems
  • Customer relationship management software
  • Chat bots


Minimal Phoenix and Elixir Dockerfile Example

Recently, I was setting up a Dockerfile for a Phoenix web app. There are official Elixir Docker images, but finding one specifically set up to handle the Phoenix Framework with asset compilation is messier territory. There’s no single set of “official” configurations.

Everybody has an opinion about how you should do this. A lot of these opinions involve adding a lot of bells and whistles. For one, their preferred solution might include Docker Compose. For another, Distillery releases. Since I had not successfully deployed my app with Docker yet at all, I wanted fewer variables to debug.

Here’s what I came up with:

FROM elixir:1.8.0-alpine

#### If needed for native dependencies
RUN apk add --no-cache make gcc libc-dev

ENV CC=gcc
ENV MAKE=cmake

RUN mix local.hex --force \
  && mix local.rebar --force

WORKDIR /root/app

ADD ./ /root/app/



RUN mix deps.get
RUN mix deps.compile
RUN mix compile
RUN mix phx.digest

CMD ["mix", "phx.server"]

This starts with the elixir alpine image. Alpine is a skinny Linux that works well in containers. I’ve found that it is a suitable base for Elixir apps. In my case, I needed a C toolchain to compile some libraries. You might not need that part. Then it sets up hex and rebar for fetching and building dependencies. Then it adds the application directory. It sets the default port and environment. It fetches the dependencies, compiles them, compiles the app, and digests the assets. Then, it starts the server. That’s it.

This approach follows the minimal instructions for a production Phoenix deployment on Docker, with no extras. From there, you can add ~complexity~ more features if you would like.

Bonus: .dockerignore for Phoenix and Elixir Projects

Here’s the .dockerignore file I use:


# Build artifacts

# Crash dumps from Erlang VM

# NPM dependencies added by asset pipeline
def create(conn, %{"post" => post_params}) do
    case Posts.create_posts(post_params) do
      {:ok, post} ->
        |> put_flash(:info, "Posts created successfully.")
        |> redirect(to: posts_path(conn, :show, post))
      {:error, %Ecto.Changeset{} = changeset} ->
          changeset: changeset,
          post_type: post_params["post_type"] || "post",
          author_options: User |> Repo.all()

Categories: code