Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Solid code isn't flexible

Solid code isn't flexible

Chris Keathley

September 02, 2022
Tweet

More Decks by Chris Keathley

Other Decks in Programming

Transcript

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

    View Slide

  2. So, I’ve been taking a break

    View Slide

  3. View Slide

  4. View Slide

  5. Y
    o
    This Talk

    View Slide

  6. This talk isn’t really
    about SOLID

    View Slide

  7. This talk is


    about building systems


    with less


    stuff

    View Slide

  8. Single Responsibility


    Open/Closed Principle


    Liskov Substitution Principle


    Interface Segregation Principle


    Dependency Inversion Principle

    View Slide

  9. Single Responsibility


    Open/Closed Principle


    Liskov Substitution Principle


    Interface Segregation Principle


    Dependency Inversion Principle
    What is this in s
    er
    vice of?

    View Slide

  10. No one ever got fired for
    adopting an acronym

    View Slide

  11. Software Design is *really hard*.

    View Slide

  12. How do we make it easy to
    change software?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. Premise 1
    Software Design is about
    managing complexity

    View Slide

  17. HTTP Client

    View Slide

  18. HTTP Client
    Socket Management

    View Slide

  19. HTTP Client
    Socket Management
    SSL

    View Slide

  20. HTTP Client
    Socket Management
    SSL
    Connection Pooling

    View Slide

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

    View Slide

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

    View Slide

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


    C
    om
    pl
    ex
    ity

    View Slide

  24. Total Complexity
    Sum(essential complexity * interaction points)

    View Slide

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

    View Slide

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

    View Slide

  27. Premise 2
    All code has a cost.


    Anything we add to the system
    should add significant value.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. Total Complexity
    Sum(essential complexity * interaction points)

    View Slide

  32. Total Complexity
    Sum(essential complexity * 0)

    View Slide

  33. Encapsulation
    Not just f
    or
    y
    ou
    r state!

    View Slide

  34. Encapsulati
    o
    API

    View Slide

  35. Encapsulati
    o
    Shall
    ow
    lay
    e
    API

    View Slide

  36. Encapsulati
    o
    Deep
    er
    Lay
    e
    API

    View Slide

  37. Encapsulati
    o
    Best Case
    API

    View Slide

  38. HTTP Client

    View Slide

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

    View Slide

  40. HTTP Client
    TCP
    MyOtherServiceClient
    Fuse Regulator

    View Slide

  41. HTTP Client
    TCP
    MyOtherServiceClient
    Fuse Regulator
    Gen
    er
    al
    Specific

    View Slide

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

    View Slide

  43. Examples
    MapSet
    Jason
    Network Protocols
    Persistent Term

    View Slide

  44. 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

    View Slide

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

    View Slide

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

    View Slide

  47. Handler
    Web Request
    Ecto
    Cache

    View Slide

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

    View Slide

  49. How much of this is
    reusable?

    View Slide

  50. Who knows how to
    make these decisions?

    View Slide

  51. Handler
    Web Request
    Ecto
    Cache

    View Slide

  52. Handler
    Web Request
    Ecto
    Cache
    Is this still reusable?

    View Slide

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

    View Slide

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

    View Slide

  55. Push specific logic upwards.


    Pull complexity downwards.

    View Slide

  56. Handler
    Ecto
    Cache
    Background
    Task

    View Slide

  57. Handler
    Ecto
    Cache
    Background
    Task

    View Slide

  58. Delete passthrough
    modules

    View Slide

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

    View Slide

  60. Handler Ecto

    View Slide

  61. Remove modules
    that provide little
    value

    View Slide

  62. 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

    View Slide

  63. 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?

    View Slide

  64. 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

    View Slide

  65. 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

    View Slide

  66. Every function
    should do one thing
    and do it completely

    View Slide

  67. Each function
    should add value
    and provide reuse.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  71. Polymorphism
    Inversion of Control
    &

    View Slide

  72. Enum

    View Slide

  73. Enum
    S
    er
    i
    ou
    sly the
    best module ev
    e

    View Slide

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

    View Slide

  75. Polymorphism in Elixir
    Functions Protocols Behaviours

    View Slide

  76. 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)

    View Slide

  77. Mentat.fetch(:posts_cache, :key, fn key ->


    case Service.get_posts(key) do


    {:ok, resp} ->


    {:commit, resp}


    {:error, e} ->


    {:ignore, e}


    end


    end)

    View Slide

  78. Polymorphism in Elixir
    Functions Protocols Behaviours

    View Slide

  79. Controller
    Web Request
    Other Service

    View Slide

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

    View Slide

  81. defprotocol MyService do


    def get_posts(impl)


    end

    View Slide

  82. 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

    View Slide

  83. 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

    View Slide

  84. real = %MyService.HTTP{}


    fake = %MyService.Mock{}


    MyService.get_posts(real)

    View Slide

  85. Controller
    Web Request
    Other Service

    View Slide

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

    View Slide

  87. defmodule MyApp.Posts do


    def index(conn, _params) do


    service = conn.private[:service]


    MyService.get_posts(service)


    end


    end

    View Slide

  88. Polymorphism in Elixir
    Functions Protocols Behaviours

    View Slide

  89. Machinery Behaviour

    View Slide

  90. Machinery Behaviour
    Behavi
    ou
    rs
    ar
    e g
    oo
    d when


    y
    ou
    c
    on
    tr ol
    the machin
    er
    y
    arou
    nd them

    View Slide

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

    View Slide

  92. Don’t forget
    to ask “why”.

    View Slide

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

    View Slide