Idiomatic, real-world Elixir resources?

This is definitely partially against my principles because I don’t believe in universal truths most of the time. But I’ve allowed myself to compile a list of those possible idiomatic good practices – which IMO go well beyond Elixir itself.

  • Use @spec. However I’ll strongly recommend against that during rapid prototyping. It will just get in the way. The moment you find a module haven’t changed in 2 weeks however, go put specs on its functions and start chasing the green output of dialyzer.

  • Complement @spec with your own guards. If you know a datatype you use is actually a list, DO NOT guard it with is_list(param). Define a guard and use that instead:

@spec orders :: [%Order{}]

defguard is_orders(x) when is_list(x)

def eligible_for_discount?(orders) when is_orders(orders) do
  #...
end
  • Do not overdo docs. Many times they should even be superfluous. If your function is well-named and the parameter names hint at their purposes clearly enough you should be just fine by putting something like Checks if this batch of orders is eligible for a corporate discount or even without docs at all. Module docs are mostly important for widely used libraries. For your own open-source pursuits and commercial projects they are mostly a distraction. Go for GitHub’s wiki articles, or Atlassian’s Confluence pages, etc.

  • Don’t use import if you can help it. Resist it with all your might. Only use it if you have no other choice and your code will not work otherwise. If you really must use it, use the :only option.

  • Don’t overdo case and cond. Use function heads with hardcoded parameter values, say def length([]), do: 0 for example. Readability often means less coding lines (though certainly not always; too much brevity can make your code very cryptic but that’s a bigger topic I won’t tackle here).

  • Use macros and don’t be shy about it – but use them sparingly. For example when you want to transcode stuff and writing all functions manually would be tedious: that is a very valid usage. Macros should be used to make an otherwise annoying code generation easier and quicker. Some here in this forum get tempted to use them to introduce their own extra syntax in Elixir but I am very against this; these efforts usually attempt to emulate another language’s constructs and rarely seem aimed at actual productivity (at least not in the way I understand it; I realise this is a controversial opinion). They are usually used to rekindle nostalgia for paradigms the author fondly remembers from other languages. I am personally strongly opposed to these temptations; if using macros can both reduce the coding lines and improve readability then I’ll use them. But I am not a fan of the LISP approach where people make their own DSLs which are perfectly suited for the project… and nobody else except the author can maintain them or the project after they are introduced in it. We have to keep our work maintainable. That’s the professional courtesy I want to show to future maintainers of my code and thus I resist using macros in Elixir beyond simple readability improvement and reducing otherwise tedious coding work.

  • GenServer, Agent and many OTP primitives and derivatives should be used for (a) keeping state and (b) making your system resilient to partial failures. If you find yourself using them for other purposes then you are abusing them and should reconsider your approach.

  • As noted above, I like @enforce_keys for structs, provided I am not in the rapid prototyping phase. I will prefer compile-time errors to runtime errors 99% of the time.

  • Some extraneous code is acceptable if it makes using your modules more ergonomic and intuitive. For example I regularly make wrapper Config modules for my apps and libraries where I expose both generic put / get functions and specific put_* and get_* functions (say, put_timeout or get_batch_maximum). I’ve had colleagues argue with me over this and I’ll concede this is a personal preference but to me the intuitive reading of my code almost as if it’s English should win over small extra amounts of code.


I am probably missing several others but hopefully this gives you an idea of what I find to be good practices. Apologies for this becoming rather huge.

13 Likes