One interesting feature of Elixir is its use of Behaviours to define implementation contracts for modules. For example we can specify a behaviour like this:

defmodule MyBehaviour do
  @callback default(params :: map) :: :ok | :error

This behaviour defines a function call default which takes a map and returns either the atom :ok or :error. You can then implement the behaviour in a model in the following way:

defmodule Test do
  @behaviour MyBehaviour

  def default(params) do

which will pass without issue. However, if you don't implement the function properly all you will get is a warning. For example if we remove the function from Test module, then the compiler will throw this warning :

warning: undefined behaviour function default/1 (for behaviour MyBehaviour)

This really is not great because warnings are lost in all the warning noise one sees after an Elixir upgrade or in libraries that have not been updated to work with the next version. To improve the quality of your code we recommend using Dialyxir, which is a type analysis tool. Running the same code will yield the following:

Undefined callback function default/1 (behaviour ''Elixir.MyBehaviour'')

But this is not all - it will even check that you return the right type. For example

def default(params) do

Will result in:

The inferred return type of default/1 ('foo') has nothing in common with 'ok' | 'error', which is the expected return type for the callback of 'Elixir.Karmavine.Behaviours.Registrar' behaviour

In our opinion it is always better to fail fast and explicitly vs. failing implicitly and without much notice.