styleguide

Elixir is a lot like Ruby on the surface, but there are some big differences. It can be a struggle to know how to style your code. Thankfully niftynate put together a style guide. I found it very helpful and have reproduced it here in a slightly friendlier format. All of the original work can be found in their repository.

Layout

You should use two spaces spaces per indentation level. No hard tabs.

def some_function do
    do_something
end
def some_function do
  do_something
end

Syntax

Use parentheses when you have arguments, no parentheses when you don't.

def some_function arg1, arg2 do
  # body omitted
end

def some_function() do
  # body omitted
end
def some_function(arg1, arg2) do
  # body omitted
end

def some_function do
  # body omitted
end

Never use do: for multi-line if/unless.

if some_condition, do:
  # a line of code
  # another line of code
  # note no end in this block
  
if some_condition do
  # some
  # lines
  # of code
end

Use do: for single-line if/unless

if some_condition, do:
  # some_stuff
if some_condition, do: # some_stuff

Never use unless with else rewrite these with the positive case first.

unless success? do
  IO.puts 'failure'
else
  IO.puts 'success'
end
if success? do
  IO.puts 'success'
else
  IO.puts 'failure'
end

Always use true as the last condition of a cond statement.

cond do
  1 + 2 == 5 ->
    "Nope"
  1 + 3 == 5 ->
    "Uh, uh"
  _ ->
    "OK"
end
cond do
  1 + 2 == 5 ->
    "Nope"
  1 + 3 == 5 ->
    "Uh, uh"
  true ->
    "OK"
end

Never put a space between a function name and the opening parenthesis.

f (3 + 2) + 1
f 3 |> g # Parses as f (3 |> g)
f(3 + 2) + 1
f(3) |> g

Omit parentheses in macro calls when a do block is passed.

quote(do
  foo
end)
quote do
  foo
end

Optionally omit parentheses in function calls (outside a pipeline) when the last argument is a function expression.

Enum.reduce 1..10, 0, fn x, acc ->
  x + acc
end
Enum.reduce(1..10, 0, fn x, acc ->
  x + acc
end)

Naming

Use snake_case for atoms, functions and variables.

:"some atom"
:SomeAtom
:someAtom

someVar = 5

def someFunction do
  ...
end

def SomeFunction do
 ...
end
:some_atom



some_var = 5

def some_function do
  ...
end




Use CamelCase for modules (Keep acronyms like HTTP, RFC, XML uppercase).

defmodule Somemodule do
  ...
end

defmodule Some_Module do
  ...
end

defmodule SomeXml do
  ...
end
defmodule SomeModule do
  ...
end

defmodule SomeXML do
  ...
end




The names of predicate functions (a function that return a boolean value) should have a trailing question mark rather than a leading is_ or similar.

def cool?(var) do
  # checks if var is cool
end

Comments

  • Write self-documenting code and ignore the rest of this section. Seriously!
  • Use one space between the leading # character of the comment and the text of the comment.
  • Comments longer than a word are capitalized and use punctuation. Use one space after periods.
String.upcase(some_string) # Capitalize string
  • Keep existing comments up-to-date. An outdated comment is worse than no comment at all.
  • Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory.

Comment Annotations

  • Annotations should usually be written on the line immediately above the relevant code.
  • The annotation keyword is followed by a colon and a space, then a note describing the problem.
  • If multiple lines are required to describe the problem, subsequent lines should be indented two spaces after the #.
  • In cases where the problem is so obvious that any documentation would be redundant, annotations may be left at the end of the offending line with no note. This usage should be the exception and not the rule.
  • Use TODO to note missing features or functionality that should be added at a later date.
  • Use FIXME to note broken code that needs to be fixed.
  • Use OPTIMIZE to note slow or inefficient code that may cause performance problems.
  • Use HACK to note code smells where questionable coding practices were used and should be refactored away.
  • Use REVIEW to note anything that should be looked at to confirm it is working as intended. For example: REVIEW: Are we sure this is how the client does X currently?
  • Use other custom annotation keywords if it feels appropriate, but be sure to document them in your project's README or similar.

Modules

Use one module per file unless the module is only used internally by another module (such as a test). Use underscored file names for CamelCase module names.

# filename is some_module.ex
defmodule SomeModule do
end

Represent each level of nesting within a module name as a directory.

# filepath is parser/core/xml_parser.ex
defmodule Parser.Core.XMLParser do
end
  • No newline after defmodule
  • No newline before first function def.
  • Newline after "module-level-code-blocks".

Documentation

Documentation in Elixir (when read either in iex with h or generated with ExDoc uses the Module Attributes @moduledoc and @doc

Always include an @moduledoc attribute the line after defmodule in your module.

defmodule SomeModule do

  @moduledoc """
  About the module
  """
  ...
end

defmodule AnotherModule do
  use SomeModule
  @moduledoc """
  About the module
  """
  ...
end
defmodule SomeModule do
  @moduledoc """
  About the module
  """
  ...
end









Use @moduledoc false if you do not intend on documenting the module.

defmodule SomeModule do
  @moduledoc false
  ...
end

Separate code after the @moduledoc with a new line.

defmodule SomeModule do
  @moduledoc """
  About the module
  """
  use AnotherModule
end

defmodule SomeModule do
  @moduledoc """
  About the module
  """

  use AnotherModule
end

Use heredocs with markdown for documentation.

defmodule SomeModule do
  @moduledoc "About the module"
end

defmodule SomeModule do
  @moduledoc """
  About the module

  Examples:
  iex> SomeModule.some_function
  :result
  """
end
defmodule SomeModule do
  @moduledoc """
  About the module

  ## Examples

      iex> SomeModule.some_function
      :result
  """
end

Strings

Match strings using the string concatenator rather than binary patterns:

<<"my"::utf8, _rest>> = "my string"
"my" <> _rest = "my string"

Metaprogramming

Avoid needless metaprogramming.

Suggested Alternatives

Suggested alternatives are styles that haven't been seen much in the community yet but might provide some value.

Cond

An atom can be used as a catch-all expression in a cond as it evaluates to a truthy value. Suggested atoms are :else or :otherwise .

cond do
  1 + 2 == 5 ->
    "Nope"
  1 + 3 == 5 ->
    "Uh, uh"
  :else ->
    "OK"
end

# is the same as
cond do
  1 + 2 == 5 ->
    "Nope"
  1 + 3 == 5 ->
    "Uh, uh"
  true ->
    "OK"
end