Embracing Elixir:
An Adoption Story 🤗

Daniel Caixinha

Software Engineer @ onfido.png

gitlab.png github.png slack.png @dcaixinha

Why?

  • Business cases matter
    • Need to share broad lessons about adoption
  • Elixir is now technically mature
  • A wider, more diverse community is always good

Elixir

elixir.png

  • Functional, dynamic programming language
  • Created in 2011 by JosĂ© Valim
  • Runs on the battle-tested Erlang VM (BEAM)
  • Actor model
    • Not Ye Olde OS processes

elixir.png

The Book

adopting_elixir.jpg

Adopting a new language… is hard!

  • Tendency to work with what's known
  • Possibly new paradigm
    • And run-time
  • Where to start?
  • Hard to build teams around it

Benefits of adopting Elixir

  • "It’s simplified our stack. Previously we used third-party integrations to help us handle our scaling needs. Now we can do what we do with just Elixir."
  • "In fact, duplexing and multiplying production traffic to a test environment showed that we can handle about ten times our current load before any response time increases."
  • – Dave Marks, Bleacher Report

bleacher_report_traffic_spikes.png bleacher_report_response_times.png

  • "When I started on the spam team, we had close to 1,400 servers running. When we converted several parts to Elixir, we reduced that by around 95% . One of the systems that ran on 200 Python servers now runs on 4 Elixir servers (it can actually run on 2 servers , but we felt that four provided more fault tolerance)."
  • "The combined effect of better architecture and Elixir saved Pinterest over $2 million per year in server costs. In addition, the performance and reliability of the systems went up despite running on drastically less hardware."
  • "When our notifications system was running on Java, it was on 30 c32.xl instances . When we switched over to Elixir, we could run on 15 . Despite running on less hardware, the response times dropped significantly, as did errors."
  • – Steve Cohen, Pinterest
  • "Performance matters to development productivity every bit as much as it matters in production. icanmakeitbetter’s test suites in Elixir all run in under 20 seconds . About two-thirds of the total codebase is written in Ruby, and runs tests in just under 10 minutes . Both suites measure 100% code coverage, and have about the same density per function. Productive developers demand fast cycles because they impact programming flow."
  • – Bruce Tate, icanmakeitbetter

Feedback cycles matter, a lot!

[app@0e04683009ec$:~/motus]$ MIX_ENV=test mix test
Excluding tags: [integration: true, pending: true]

............................................................................................................................................................

Finished in 7.1 seconds
822 tests, 0 failures, 0 skipped

Randomized with seed 799264

Adopting a new language

  • Creating Your First Application
  • Ensuring Code Consistency
  • Team Building

Creating Your First Application

  • Where to start? A single, focused problem that's:
  • Central enough to be seen, big enough to matter
  • Ideally has business value

Creating Your First Application

  • "Ben [Marx] first worked on a tiny service that fetched titles, descriptions, and the like from external services. By fetching all of the metadata concurrently instead of doing each request sequentially, his tiny team reduced the response time dramatically . The simple service took a day to prototype, a few more to finish out and deploy, and solved a key business need ."
  • "After an initial prototype, there was a finished application, a win for the business advocates, and a small and growing list of assets in new leaders and code."
  • – Adopting Elixir [2018]

Creating Your First Application

  • Something that would make use of Elixir's strengths
  • Some part of a legacy app that needs to scale further

Heimdall

  • 1st Elixir app deployed to production at Onfido
  • Make async requests look sync to the outside world
  • Very hard to do with our previous tech stack
def generate_report(conn) do
  result =
    Helper.query_string_params(conn)
    |> perform_request()

  conn
  |> json_response
  |> Plug.Conn.send_resp(response_code, result)
end

Loki

  • The Sandbox Service
  • Through metaprogramming, a new report type can be added without coding
  • Has been so reliable I often forget that it's there
# standard.json.eex
{
  "data": {
    "score": "<%= provider_score %>"
  }
}
# provider_standard.exs
%{
  template: "standard.json.eex",
  etl_matches: %{
    "provider_score" => %{clear: "0.0", consider: "1.0"}
  }
}

Imago

  • Service that enables the download/upload of media across Onfido
  • Previous solution wasn't scaling
  • Takes care of encryption/decryption, interacting with S3, and caching!

Imago

imago.png

Request count and Latency

imago_total_downloads.png imago_response_times.png

Memory Usage

imago_memory_usage.png

If you can't find a candidate…

  • Try the Terraform library
  • Start by extracting the auth system
defmodule MyApp.Terraformers.Foo do
  alias MyApp.Clients.Foo
  use Plug.Router

  plug :match
  plug :dispatch

  # match specific path
  get "/v1/hello-world", do: send_resp(conn, 200, "This was handled by Elixir!")

  # match all GETs
  get _ do
    %{method: "GET", request_path: request_path, params: params, req_headers: req_headers} = conn
    res = Foo.get!(request_path, req_headers, [params: Map.to_list(params)])
    send_response({:ok, conn, res})
  end

  defp send_response({:ok, conn, %{headers: headers, status_code: status_code, body: body}}) do
    conn = %{conn | resp_headers: headers}
    send_resp(conn, status_code, body)
  end
end

Testing it All

  • Tests will allow you to have greater confidence on the migration
  • Code coverage doesn't mean a lot on its own
    • But it allows you to check for blind spots
  • Having a great test suite will give newcomers confidence to start contributing
  • Test everything on a QA/pre-prod environment before going live

Ensuring Code Consistency

  • Every application is doomed to become legacy…
    • Preventing it requires proactive and continuous work!
  • Be diligent, so that the code stays fresh, and the coding stays fun
  • Elixir has some pretty good tools to help with this:
    • Formatter
    • Credo
    • Dialyzer
    • Doctests
defmodule Demo do
  @moduledoc """
  Documentation for Demo.
  """

  @doc """
  Hello world.

  ## Examples

      iex> Demo.hello()
      :world

  """
  def hello do
    :world
  end
end

Ensuring Code Consistency

  • Let the computer do things computers are good at
  • Let your team focus on the issues that matter

Team Building

After your first prototype is a hit, you'll need to
think about growing a team!

  • Hiring
  • Training in-house

Training in-house

  • Not easy, but definitely possible
  • At each step, look for chances to pair, review, or mentor
  • Pair newcomers with experienced developers
  • Give ownership of simpler features to new developers
  • "Bleacher Report got the team a copy of Programming Elixir 1.3 by Dave Thomas and gave them some office time to start working through it, alone or in groups. Slowly the team built an overall feel for the language."
  • – Adopting Elixir [2018]
  • Start an Elixir guild, if possible

BEAM Me Up, Scotty!

Training in-house

  • Projects hitting production will likely spark enthusiasm across the team(s)
  • An application with consistent style will be easier for newcomers
  • Try to manage technology adoption by consensus instead of by decree
  • No need to train everybody at once!
  • "At Bleacher Report, they only had 2 to 4 full-time Elixir developers in the first year . This strategy gave developers time to learn the language and get the prototype apps into production."
  • – Adopting Elixir [2018]

There's much more to adoption…

I've been talking about Elixir…

…but most points are common for every adoption

Wrapping Up

  • Build your business case
  • Create consistent processes around your new language
  • Make a plan to build/train your team(s)

Alchemists at onfido.png

  • paulo.png
  • elixir_cookbook.jpg
  • joao.png
  • advanced_elixir_and_otp.png
  • serrano.png
  • elixirconf_eu.png
  • andre.png & daniel.png
  • mastering_elixir.png

lisbon_elixir.png

Thank you! 🙇

Questions?

caixinha.pt

gitlab.png github.png slack.png @dcaixinha