Slide 1

Slide 1 text

mkaszubowski94 Maciej Kaszubowski ERROR-FREE ELIXIR

Slide 2

Slide 2 text

mkaszubowski94 Error handling is crucial

Slide 3

Slide 3 text

mkaszubowski94 Error handling is a big source of complexity

Slide 4

Slide 4 text

mkaszubowski94 def cancel_job(user_id, job_id) do Job  Repo.get(job_id)  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() end

Slide 5

Slide 5 text

mkaszubowski94 def cancel_job(user_id, job_id) do with {:ok, job}  fetch_job(job_id), :ok  can_cancel?(job, user_id) do job  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() else nil  {:error, :not_found} {:error, :already_canceled}  {:error, :already_canceled} {:error, :already_completed}  {:error, :already_completed} {:error, :no_permissions}  {:error, :no_permissions} {:error, %Changeset{} = changeset}  {:error, changeset} end end

Slide 6

Slide 6 text

mkaszubowski94 When designing, we focus mostly on the happy path.

Slide 7

Slide 7 text

mkaszubowski94 We have to design error-handling too.

Slide 8

Slide 8 text

mkaszubowski94 Not errors are equal

Slide 9

Slide 9 text

mkaszubowski94 Users Sources of errors

Slide 10

Slide 10 text

mkaszubowski94 Users Other services Sources of errors

Slide 11

Slide 11 text

mkaszubowski94 Users Infrastructure Other services Sources of errors

Slide 12

Slide 12 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors

Slide 13

Slide 13 text

mkaszubowski94 Each requires a different error-handling strategy

Slide 14

Slide 14 text

Strategies

Slide 15

Slide 15 text

mkaszubowski94 Let it crash!

Slide 16

Slide 16 text

mkaszubowski94 Let it crash!

Slide 17

Slide 17 text

mkaszubowski94 Infrastructure Other services Let it crash! • Incorrect behaviour • Bugs hard to reproduce • Bugs hard to recover from What?

Slide 18

Slide 18 text

mkaszubowski94 Infrastructure Other services Let it crash! • Incorrect behaviour • Bugs hard to reproduce • Bugs hard to recover from What? Why? • Protect invariants • Restarting can help

Slide 19

Slide 19 text

mkaszubowski94 :error tuples

Slide 20

Slide 20 text

mkaszubowski94 :error tuples

Slide 21

Slide 21 text

mkaszubowski94 • User/Client errors • Correct behaviour (!) • Predicable errors What? Users Other services :error tuples

Slide 22

Slide 22 text

mkaszubowski94 • User/Client errors • Correct behaviour (!) • Predicable errors What? Why? • Client can interpret the error • Client can recover from errors Users Other services :error tuples

Slide 23

Slide 23 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors

Slide 24

Slide 24 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors

Slide 25

Slide 25 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors

Slide 26

Slide 26 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors

Slide 27

Slide 27 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors ?

Slide 28

Slide 28 text

mkaszubowski94 Developers Users Infrastructure Other services Sources of errors :(

Slide 29

Slide 29 text

mkaszubowski94 Controller Context Context.Xxx

Slide 30

Slide 30 text

mkaszubowski94 Controller Context Context.Xxx

Slide 31

Slide 31 text

mkaszubowski94 We're too used to the "standard" ways

Slide 32

Slide 32 text

mkaszubowski94 case some_function(args) do {:ok, something}  #  {:error, reason}  {:error, reason} end

Slide 33

Slide 33 text

mkaszubowski94 Things to consider Can the layer above interpret the error? 1 2 How is the error possible? 3 Can the layer above fix the error?

Slide 34

Slide 34 text

mkaszubowski94 Don't deal with errors. Prevent them.

Slide 35

Slide 35 text

Eliminating errors

Slide 36

Slide 36 text

mkaszubowski94 Heuristics Declarative operations 1 2 Reasonable defaults 3 Don't fight the real world

Slide 37

Slide 37 text

mkaszubowski94 Heuristics Declarative operations 1 2 Reasonable defaults 3 Don't fight the real world

Slide 38

Slide 38 text

mkaszubowski94 defmodule Accounts do @spec get_settings(id())  {:ok, t()} | {:error, :not_found} def get_settings(user_id) do case Repo.get_by(Settings, user_id: user_id) do %Settings{} = settings  {:ok, settings} nil  {:error, :not_found} end end end

Slide 39

Slide 39 text

mkaszubowski94 defmodule Accounts do @spec get_settings(id())  {:ok, t()} | {:error, :not_found} def get_settings(user_id) do case Repo.get_by(Settings, user_id: user_id) do %Settings{} = settings  {:ok, settings} nil  {:error, :not_found} end end end

Slide 40

Slide 40 text

mkaszubowski94 defmodule Accounts do @spec get_settings(id())  {:ok, t()} def get_settings(user_id) do case Repo.get_by(Settings, user_id: user_id) do %Settings{} = settings  {:ok, settings} nil  {:ok, Settings.default()} end end end

Slide 41

Slide 41 text

mkaszubowski94 Heuristics Declarative operations 1 2 Reasonable defaults 3 Don't fight the real world

Slide 42

Slide 42 text

mkaszubowski94 def cancel_job(user_id, job_id) do with {:ok, job}  fetch_job(job_id), :ok  can_cancel?(job, user_id) do job  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() else nil  {:error, :not_found} {:error, :already_canceled}  {:error, :already_canceled} {:error, :already_completed}  {:error, :already_completed} {:error, :no_permissions}  {:error, :no_permissions} {:error, %Changeset{} = changeset}  {:error, changeset} end end

Slide 43

Slide 43 text

mkaszubowski94 Describe the end goal, not the steps to reach it

Slide 44

Slide 44 text

mkaszubowski94 def cancel_job(user_id, job_id) do with {:ok, job}  fetch_job(job_id), :ok  can_cancel?(job, user_id) do job  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() else nil  {:error, :not_found} {:error, :already_canceled}  {:error, :already_canceled} {:error, :already_completed}  {:error, :already_completed} {:error, :no_permissions}  {:error, :no_permissions} {:error, %Changeset{} = changeset}  {:error, changeset} end end

Slide 45

Slide 45 text

mkaszubowski94 def cancel_job(user_id, job_id) do with {:ok, job}  fetch_job(job_id), :ok  can_cancel?(job, user_id) do job  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() else nil  {:error, :not_found} {:error, :already_canceled}  {:error, :already_canceled} {:error, :already_completed}  {:error, :already_completed} {:error, :no_permissions}  {:error, :no_permissions} {:error, %Changeset{} = changeset}  {:error, changeset} end end

Slide 46

Slide 46 text

mkaszubowski94 def cancel_job(user_id, job_id) do with {:ok, job}  fetch_job(job_id), :ok  can_cancel?(job, user_id) do job  Job.cancel_changeset(%{user_id: user_id, canceled_at: now()})  Repo.update() else nil  {:error, :not_found} {:error, :already_canceled}  {:ok, job} {:error, :already_completed}  {:error, :already_completed} {:error, :no_permissions}  {:error, :no_permissions} {:error, %Changeset{} = changeset}  {:error, changeset} end end

Slide 47

Slide 47 text

mkaszubowski94 Heuristics Declarative operations 1 2 Reasonable defaults 3 Don't fight the real world

Slide 48

Slide 48 text

mkaszubowski94 def take_off_shelf(product_id, shelf_id) do if product_on_the_shelf?(product_id, shelf_id) do remove_from_shelf!(product_id, shelf_id) else {:error, :product_missing} end end

Slide 49

Slide 49 text

mkaszubowski94 def take_off_shelf(product_id, shelf_id) do if product_on_the_shelf?(product_id, shelf_id) do remove_from_shelf!(product_id, shelf_id) else {:error, :product_missing} end end

Slide 50

Slide 50 text

mkaszubowski94 def take_off_shelf(product_id, shelf_id) do if product_on_the_shelf?(product_id, shelf_id) do remove_from_shelf!(product_id, shelf_id) else notify_operators(product_id, shelf_id) end end

Slide 51

Slide 51 text

mkaszubowski94 We cannot prevent people from making mistakes

Slide 52

Slide 52 text

mkaszubowski94 Not all errors are bad

Slide 53

Slide 53 text

mkaszubowski94 Error handling has to be designed

Slide 54

Slide 54 text

Why talk about this?

Slide 55

Slide 55 text

mkaszubowski94 Design is not about a single clever idea

Slide 56

Slide 56 text

mkaszubowski94 We cannot rely on intuition

Slide 57

Slide 57 text

mkaszubowski94 You will make wrong decisions Make sure you learn from that.

Slide 58

Slide 58 text

mkaszubowski94 A set of clear and simple heuristics and questions is really helpful

Slide 59

Slide 59 text

mkaszubowski94 Approach design more thoughtfully

Slide 60

Slide 60 text

mkaszubowski94 Use facts instead of opinions

Slide 61

Slide 61 text

mkaszubowski94 Gather as many questions and heuristics as possible.

Slide 62

Slide 62 text

mkaszubowski94 Gather as many questions and heuristics as possible. And share them!

Slide 63

Slide 63 text

mkaszubowski94 Share the process of making decisions.

Slide 64

Slide 64 text

mkaszubowski94 "A Philosophy of Software Design" - John Ousterhout "The art of destroying software" - Greg Young Credits

Slide 65

Slide 65 text

mkaszubowski94 Maciej Kaszubowski THANKS! I hope it was useful