Slide 1

Slide 1 text

Chris Keathley / @ChrisKeathley / [email protected] SOLID code isn’t Flexible

Slide 2

Slide 2 text

So, I’ve been taking a break

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Y o This Talk

Slide 6

Slide 6 text

This talk isn’t really about SOLID

Slide 7

Slide 7 text

This talk is about building systems with less stuff

Slide 8

Slide 8 text

Single Responsibility Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle

Slide 9

Slide 9 text

Single Responsibility Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle What is this in s er vice of?

Slide 10

Slide 10 text

No one ever got fired for adopting an acronym

Slide 11

Slide 11 text

Software Design is *really hard*.

Slide 12

Slide 12 text

How do we make it easy to change software?

Slide 13

Slide 13 text

How do we make it easy to change software? How do we add new functionality without changing any existing code?

Slide 14

Slide 14 text

Lets Talk About: Fundamental Ideals Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 15

Slide 15 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 16

Slide 16 text

Premise 1 Software Design is about managing complexity

Slide 17

Slide 17 text

HTTP Client

Slide 18

Slide 18 text

HTTP Client Socket Management

Slide 19

Slide 19 text

HTTP Client Socket Management SSL

Slide 20

Slide 20 text

HTTP Client Socket Management SSL Connection Pooling

Slide 21

Slide 21 text

HTTP Client Socket Management SSL Connection Pooling h1.1 vs h2

Slide 22

Slide 22 text

HTTP Client Socket Management SSL Connection Pooling h1.1 vs h2 Parsing http payloads

Slide 23

Slide 23 text

HTTP Client Socket Management SSL Connection Pooling h1.1 vs h2 Parsing http payloads Essential C om pl ex ity

Slide 24

Slide 24 text

Total Complexity Sum(essential complexity * interaction points)

Slide 25

Slide 25 text

Total Complexity Sum(essential complexity * interaction points) Most often in the f or m of dependent code

Slide 26

Slide 26 text

Dependencies Change h er e… Module A Module B Means Change h er e

Slide 27

Slide 27 text

Premise 2 All code has a cost. Anything we add to the system should add significant value.

Slide 28

Slide 28 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 29

Slide 29 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 30

Slide 30 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 31

Slide 31 text

Total Complexity Sum(essential complexity * interaction points)

Slide 32

Slide 32 text

Total Complexity Sum(essential complexity * 0)

Slide 33

Slide 33 text

Encapsulation Not just f or y ou r state!

Slide 34

Slide 34 text

Encapsulati o API

Slide 35

Slide 35 text

Encapsulati o Shall ow lay e API

Slide 36

Slide 36 text

Encapsulati o Deep er Lay e API

Slide 37

Slide 37 text

Encapsulati o Best Case API

Slide 38

Slide 38 text

HTTP Client

Slide 39

Slide 39 text

HTTP Client TCP Y ou sh ou ld on ly have to see this stuff Not this…

Slide 40

Slide 40 text

HTTP Client TCP MyOtherServiceClient Fuse Regulator

Slide 41

Slide 41 text

HTTP Client TCP MyOtherServiceClient Fuse Regulator Gen er al Specific

Slide 42

Slide 42 text

HTTP Client TCP MyOtherServiceClient Fuse Regulator Gen er al Specific M or e Reusable Less Reusable

Slide 43

Slide 43 text

Examples MapSet Jason Network Protocols Persistent Term

Slide 44

Slide 44 text

Red Flags Excessive passthrough or defdelegates Layered modules that share similar apis or functions Changes impacting multiple dependencies Pushing specific details into lower levels decreases reuse

Slide 45

Slide 45 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 46

Slide 46 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 47

Slide 47 text

Handler Web Request Ecto Cache

Slide 48

Slide 48 text

Handler Web Request Ecto Cache Wh er e do these go?

Slide 49

Slide 49 text

How much of this is reusable?

Slide 50

Slide 50 text

Who knows how to make these decisions?

Slide 51

Slide 51 text

Handler Web Request Ecto Cache

Slide 52

Slide 52 text

Handler Web Request Ecto Cache Is this still reusable?

Slide 53

Slide 53 text

Handler Ecto Cache Can I use this functi on ? Background Task

Slide 54

Slide 54 text

Handler Ecto Cache Are we g oi ng to need to tune this? Background Task

Slide 55

Slide 55 text

Push specific logic upwards. Pull complexity downwards.

Slide 56

Slide 56 text

Handler Ecto Cache Background Task

Slide 57

Slide 57 text

Handler Ecto Cache Background Task

Slide 58

Slide 58 text

Delete passthrough modules

Slide 59

Slide 59 text

Handler Ecto Wrapper defmodule EctoWrapper do def one(query), do: Repo.one(query) def all(query), do: Repo.all(query) end

Slide 60

Slide 60 text

Handler Ecto

Slide 61

Slide 61 text

Remove modules that provide little value

Slide 62

Slide 62 text

def get_posts(id) do case :fuse.check(:service) do :ok -> case call_service(id) do {:ok, result} -> :ok = Cache.put(id, result) {:ok, result} {:error, error} -> :fuse.melt(:service) {:error, error} end :blown -> cached = Cache.get(id) if cached do {:ok, result} else {:error, error} end end end

Slide 63

Slide 63 text

def get_posts(id) do case :fuse.check(:service) do :ok -> case call_service(id) do {:ok, result} -> :ok = Cache.put(id, result) {:ok, result} {:error, error} -> :fuse.melt(:service) {:error, error} end :blown -> cached = Cache.get(id) if cached do {:ok, result} else {:error, error} end end end H ow does this s er vice int er act with the cache?

Slide 64

Slide 64 text

def get_posts(id) do case :fuse.check(:service) do :ok -> case call_service(id) do {:ok, result} -> :ok = Cache.put(id, result) {:ok, result} {:error, error} -> :fuse.melt(:service) {:error, error} end :blown -> cached = Cache.get(id) if cached do {:ok, result} else {:error, error} end end end Finding a bug means reading the functi o

Slide 65

Slide 65 text

def get_posts(id) do case :fuse.check(:service) do :ok -> case call_service(id) do {:ok, result} -> :ok = Cache.put(id, result) {:ok, result} {:error, error} -> :fuse.melt(:service) {:error, error} end :blown -> cached = Cache.get(id) if cached do {:ok, result} else {:error, error} end end end This is a “C om plete” Functi o

Slide 66

Slide 66 text

Every function should do one thing and do it completely

Slide 67

Slide 67 text

Each function should add value and provide reuse.

Slide 68

Slide 68 text

A function’s signature should be much simpler than its implementation.

Slide 69

Slide 69 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 70

Slide 70 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 71

Slide 71 text

Polymorphism Inversion of Control &

Slide 72

Slide 72 text

Enum

Slide 73

Slide 73 text

Enum S er i ou sly the best module ev e

Slide 74

Slide 74 text

Enum Enum er able things go in Functi on s that op er ate on items

Slide 75

Slide 75 text

Polymorphism in Elixir Functions Protocols Behaviours

Slide 76

Slide 76 text

defmodule Animal do def speak(f) do f.() end end dog = fn -> "bark" end cat = fn -> "Worship me you lowly human" end Animal.speak(dog) Animal.speak(cat)

Slide 77

Slide 77 text

Mentat.fetch(:posts_cache, :key, fn key -> case Service.get_posts(key) do {:ok, resp} -> {:commit, resp} {:error, e} -> {:ignore, e} end end)

Slide 78

Slide 78 text

Polymorphism in Elixir Functions Protocols Behaviours

Slide 79

Slide 79 text

Controller Web Request Other Service

Slide 80

Slide 80 text

Controller Web Request Other Service Can we mock this ou t?

Slide 81

Slide 81 text

defprotocol MyService do def get_posts(impl) end

Slide 82

Slide 82 text

defprotocol MyService do def get_posts(impl) end defmodule MyService.HTTP do defstruct :url, :fuse defimpl MyService do def get_posts(%{url: url, fuse: fuse}) do case :fuse.check(fuse) do :ok -> Req.get(url) :blown -> {:error, Error.unavailable("Service is down")} end end end end

Slide 83

Slide 83 text

defprotocol MyService do def get_posts(impl) end defmodule MyService.Mock do defstruct fake_response: %{} defimpl MyService do def get_posts(%{fake_response: fake_response}) do fake_response end end end

Slide 84

Slide 84 text

real = %MyService.HTTP{} fake = %MyService.Mock{} MyService.get_posts(real)

Slide 85

Slide 85 text

Controller Web Request Other Service

Slide 86

Slide 86 text

Controller Web Request Other Service Inject the s tr uct into the plug c on n

Slide 87

Slide 87 text

defmodule MyApp.Posts do def index(conn, _params) do service = conn.private[:service] MyService.get_posts(service) end end

Slide 88

Slide 88 text

Polymorphism in Elixir Functions Protocols Behaviours

Slide 89

Slide 89 text

Machinery Behaviour

Slide 90

Slide 90 text

Machinery Behaviour Behavi ou rs ar e g oo d when y ou c on tr ol the machin er y arou nd them

Slide 91

Slide 91 text

Lets Talk About: Fundamental Ideas Deep Layers vs Shallow Layers Things are better together Allowing for extension

Slide 92

Slide 92 text

Don’t forget to ask “why”.

Slide 93

Slide 93 text

Chris Keathley / @ChrisKeathley / [email protected] Thanks!