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

Gardening An Elixir Application

Gardening An Elixir Application

Andrew Summers

February 19, 2019
Tweet

More Decks by Andrew Summers

Other Decks in Programming

Transcript

  1. whoami • Backend Software Engineer at albert.io • Wrote Dialyzer

    pretty printer • Maintainer of Dialyxir, Erlex, elixir-lsp packages • Contributor to many others! • Not a gardener
  2. What is this? • Opinionated patterns learned from • writing

    and refactoring a lot of Elixir • developer confusion • tricky bugs • my particular tastes
  3. • For many projects this may be enough • Don't

    over-engineer if unnecessary • Conceptual scale is often as important as code scale
  4. Control Growth! • Control where and how modules can grow

    • Maximize compiler help when patterns change • Minimize errors of forgetfulness • Write Assertive Code 
 http://blog.plataformatec.com.br/2014/09/writing- assertive-code-with-elixir/
  5. defstruct • Use structs in combination with behaviors • Avoid

    impromptu data structures, e.g. maps when something has a name • Especially useful at ingress/egress boundaries • Match and dispatch on struct module • Avoid creating too early • Do not make unnecessary structs
  6. @behaviour • See Alistair Cockburn Ports/Adapter pattern • Use @impl,

    avoid @impl true • Useful with Mox for stubbing mocks • Useful when passing around modules in generic pipelines
  7. Modules as data structures • See: Ecto's __schema__/1 function •

    Can be behaviourized for compile/refactor safety • Allows passing modules and asking them questions about how to execute generic pipelines, e.g. what are your attributes • Powerful in combination with Domain Specific Languages
  8. defprotocol • Useful when data structures may exist in other

    projects • Ability to extend behavior of existing modules • Inspect, Enumerable as examples in Elixir core
  9. Interrobang?! • Use the availability of ? and ! in

    variable, function names • Unless guard, or over network, avoid is_ prefix • Use ! when function may raise, but be consistent • If ! function is provided, have a non-! function that returns :ok/:error tuple
  10. Dependencies • Try to avoid wherever possible, but don't NIH

    them • Think of them like herbicides for plants - can help but can take over your entire garden if left unchecked • Keep roots away from main application with wrappers • Hard to rip out after they permeate app, especially when transitively included by other dependencies
  11. Good Root Structure • Use mix xref to examine how

    modules fit together • Use utility style modules when "leaf" functionality • Keep direction pointing down • Similar granularity layers should interact with each other • Prefer "deep modules" as in Philosophy of Software Design
  12. Optimize for greppability • File name should match module name

    • Avoid partial and multi aliases • Avoid alias :as when possible • alias > import • Prefer import :only • Try to not import in __using__ macros
  13. Macros • Only do necessary work in macros • Delegate

    to functions ASAP for debuggability • Use location: :keep • after_compile validation useful in DSLs • Disabling lexical tracker useful for preventing deadlocks • Avoid name generation • Clean up after yourself
  14. @spec All The Things • Dialyzer is an imperfect tool

    • But helps prevent classes of errors if used in CI • @spec helps humans as much as CI tooling • Credo rules exist to enforce @spec on all functions
  15. Wait For Pain Points • Always keep eye out for

    abstractions that need to exist to solve some problem • But make sure there's a problem first • Wrong abstractions are worse than code duplication
 
 https://www.sandimetz.com/blog/2016/1/20/the-wrong- abstraction
  16. Aggressively Refactor • Refactor for clarity • Remove unused functions

    • defp gives warnings when unused, can be inlined • When removing function call, check for dead code • Reevaluate code for unnecessary abstractions
  17. GenServers & Umbrellas • Processes are not for domain logic

    organization
 https://www.theerlangelist.com/article/spawn_or_not • Should call into application logic where appropriate • Umbrellas are not for code organization (ultimately) • Both are for runtime concerns