Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Only possible with Elixir UBOTS Case Study

Slide 3

Slide 3 text

My name is Tymon Tobolski. I'm the one behind tesla HTTP library. Co-founder of UBOTS - Useful bots for Slack @teamon @ GitHub @iteamon @ Twitter

Slide 4

Slide 4 text

Ruby ➡ Elixir (ASM, C, PHP, Java, Scala, Haskell, JavaScript, Go, Infra, ...)

Slide 5

Slide 5 text

How to build something exclusively with Elixir?

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

UI = JSON ‑ ‑ ‑ ! =

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Adam ! Development @ Ubots Head of Integrations @ Recruitee Kamil Marketing @ Ubots SRE @ Cabify

Slide 11

Slide 11 text

Why we succeeded?

Slide 12

Slide 12 text

Why we succeeded? We didn't quit our jobs We didn't move to Silicon Valley We didn't take VC money

Slide 13

Slide 13 text

Why we succeeded? We did charge $$$ from day one We chose Elixir

Slide 14

Slide 14 text

Developer Productivity

Slide 15

Slide 15 text

Elixir is Extremely Stable

Slide 16

Slide 16 text

Seamless Updates 1.9 ➡ 1.10 ➡ 1.11

Slide 17

Slide 17 text

Best Tooling mix, hex, dialyzer, formatter phoenix live reload, mix test.watch ElixirLS

Slide 18

Slide 18 text

!! != One Git repository One OTP application One Erlang release One Docker image + Two Developers ------------------------------ One Majestic Monolith

Slide 19

Slide 19 text

Top-Level Modules lib !"" core/ !"" core.ex !"" core_web/ !"" core_web.ex Core CoreWeb lib !"" bdgt/ !"" bdgt.ex !"" lunch/ !"" lunch.ex Bdgt Lunch lib !"" queue/ !"" queue.ex !"" td/ #"" td.ex Queue Td

Slide 20

Slide 20 text

This must be slow lib/ 21.000 LOC $ time mix compile Compiling 277 files (.ex) real 0m34.552s test/ 10.000 LOC $ mix test Finished in 17.1 seconds 18 doctests, 483 tests

Slide 21

Slide 21 text

the BORING stack

Slide 22

Slide 22 text

Old Patterns New "Framework"

Slide 23

Slide 23 text

HTTP POST /events content-type: application/json { "payload": { "user_id": "U123", "team_id": "T456" "command": "/help" } } --- POST /events content-type: text/form-urlencoded user_id=U123&team_id=T456&command=/help Core.Bot %Context{ user_id: "U123", team_id: "T456", responses: [...] ... } # Actions %Command{ command: "/queue", text: "help" } %Button{value: "join"} %Shortcut{...} ...

Slide 24

Slide 24 text

Ubots Slack "Framework" Web App Slack App Plug.Conn Context Struct params Action Structs Plug Middleware Phoenix.Router (Pattern Matching) Phoenix.Controller Controller Phoenix.View View

Slide 25

Slide 25 text

Pure Functional Code

Slide 26

Slide 26 text

View (pure function) defmodule Queue.Views.ModalConfig do def render(queue) do %{ type: "modal", blocks: [...] } end end

Slide 27

Slide 27 text

Impure Functional Code (side effects are cool)

Slide 28

Slide 28 text

Middleware (impure function) defmodule Core.Bot.Middleware.Logger do def call(ctx, action) do Logger.info(...) ctx end end

Slide 29

Slide 29 text

Pattern Matching

Slide 30

Slide 30 text

Pattern Matching in Actions ( ) def build(%{ "command" => command, "text" => text }) do %Command{command: command, text: text} end def build(%{ "type" => "shortcut", "callback_id" => callback_id }) do %Shortcut{callback_id: callback_id} end def build(%{ "type" => "view_submission", "view" => %{ "callback_id" => callback_id, "external_id" => external_id, "state" => %{"values" => values} } }) do %Submit{ callback_id: callback_id, external_id: decode_external_id(external_id), values: decode_values(values) } end

Slide 31

Slide 31 text

Pattern Matching in Controllers defmodule Queue.Controllers.Main do use Core.Bot.Controller def call(ctx, %Button{action_id: "button_join"}), do: # ... def call(ctx, %Command{text: "join"}), do: # ... def call(ctx, %Button{action_id: "button_leave"}), do: # ... def call(ctx, %Command{text: "leave"}), do: # ...

Slide 32

Slide 32 text

Macros (just some drops)

Slide 33

Slide 33 text

Macros for Controller Pipelines defmodule Queue do use Core.Bot.Controller plug Core.Bot.Middleware.Logger plug Core.Bot.Middleware.Ignorables plug Core.Bot.Middleware.Duplicates plug Core.Bot.Middleware.Support plug Queue.Controllers.Main plug Queue.Controllers.Home plug Queue.Controllers.Workflows # ...

Slide 34

Slide 34 text

Macros for DRY defmodule Core.Bot.Actions do use Core.Bot.ActionsBuilder defaction(Button, action_id: binary, value: binary ) defaction(Datepicker, action_id: binary, action_ts: binary, block_id: binary, initial_date: binary | nil, selected_date: binary )

Slide 35

Slide 35 text

Processes & OTP

Slide 36

Slide 36 text

Phoenix, Oban, ... got your back

Slide 37

Slide 37 text

Unleashing the full potential setup do [ alice: Slack.user(), dev: Slack.channel() ] end test "/queue", %{alice: alice, dev: dev} do alice |> go_to_channel(dev) |> command("/queue") |> click_button("Join") assert visible?(dev, "People in queue") end

Slide 38

Slide 38 text

Unleashing the full potential

Slide 39

Slide 39 text

Refactoring

Slide 40

Slide 40 text

Lax.Server (Single GenSever) defmodule Lax.Server do use GenServer # ... def handle_call({:get_channel, channel_id}, from, state) do handle_reply(Lax.State.get_channel(state, channel_id), from) end def handle_call({:access, action}, from, state) do handle_reply(Lax.Access.on(action, state), from) end def handle_call({:ui, action}, from, state) do handle_reply(Lax.UI.on(action, state), from) end def handle_call({:outgoing, ...}) do handle_reply(Lax.API.on(method, body, state), from) end # ... end ... The chery on top of all elixir features is the amazing community

Slide 41

Slide 41 text

Friendly Community & Smart People

Slide 42

Slide 42 text

Is this really only possible with Elixir?

Slide 43

Slide 43 text

10 out of 10 boxes say: YES! ✅ Tooling ✅ Stability ✅ Readability ✅ Productivity ✅ Developer Experience ✅ Rich ecosystem ✅ Performance ✅ Scalability ✅ Community ✅ Great choice for business

Slide 44

Slide 44 text

! Q & A ! Visit ubots.xyz for Useful Slack Bots ! Sign up at moonhome.io for the Future of Remote Development ! Follow @iteamon on Twitter to stay up-to-date

Slide 45

Slide 45 text

No content