Shared module constants

Using such macros has still the same issue - you have opaque integers passing around at runtime - this makes it really hard to debug issues, especially if dealing with large numbers.

As to when do conversions - I spoke about boundaries of the system. By that I mean every time we receive data from outside or send it out - this means params from requests, database responses, etc. For example, when using ecto, you can provide a custom type to do the conversions for you (assuming encode and decode functions as before):

defmodule Constant do
  @behaviour Ecto.Type

  def type, do: :integer

  def cast(int) when is_integer(int), do: {:ok, decode(value)}
  def cast(atom) when is_atom(atom), do: {:ok, atom)
  def cast(_), do: :error

  def load(int) when is_integer(int), do: {:ok, decode(value)}
  def load(_), do: :error

  def dump(atom) when is_atom(atom), do: {:ok, encode(value)}
  def dump(_), do: :error
end