Skip to content

Latest commit

 

History

History
405 lines (352 loc) · 8.87 KB

StyleGuide.md

File metadata and controls

405 lines (352 loc) · 8.87 KB

Style Guide

A programmer does not primarily write code; rather, he primarily writes to another programmer about his problem solution. The understanding of this fact is the final step in his maturation as technician.

What a Programmer Does, 1967

This style guide represents guidelines for writing good Erlang code according to the authors of erlfmt. It consists of two sections:

  • Linting - with guidelines about code content that are considered good practice and need to be applied manually (or using rebar3_lint / elvis)
  • Formatting - with guidelines about code layout that can be applied automatically by using erlfmt

Linting

Naming

  • Use CamelCase for variables (acronyms are fine both ways)
%% Bad
File_name
File_Name
_file
Stored_id
%% Good
FileName
_File
StoredID
StoredId

Related elvis configuration:

{elvis_style, variable_naming_convention, #{regex => "^([A-Z][0-9a-zA-Z]*)$"}}
  • Use snake_case for functions, attributes, and atoms
%% Bad
'Error'
badReturn
read_File
%% Good
error
bad_return
read_file

Related elvis configuration:

{elvis_style, module_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$"}},
{elvis_style, function_naming_convention, #{regex => "^([a-z]*_?)*$"}}
  • Use SCREAMING_SNAKE_CASE for macro definitions
%% Bad
-define(some_value, 100).
-define(someMacro(), hidden:call()).
-define(AnotherMacro, "another macro").
%% Good
-define(SOME_VALUE, 100).
-define(SOME_MACRO(), hidden:call()).
-define(ANOTHER_MACRO, "another macro").

Related elvis configuration:

{elvis_style, macro_names}
  • Omit () in macro names only if they represent constants
%% Bad
-define(NOT_CONSTANT, application:get_env(myapp, key)).
-define(CONSTANT(), 100).
%% Good
-define(NOT_CONSTANT(), application:get_env(myapp, key)).
-define(CONSTANT, 100).
  • Start names of predicate functions (functions that return a boolean) with is_ or has_ prefix
%% Bad
evenp(Num) -> ...
%% Good
is_even(Num) -> ...
  • Avoid using one-letter variable names and do_ prefixes for functions, prefer descriptive names
%% Bad
validate([X | Xs]) ->
    do_validate(X),
    validate(Xs).
%% Good
validate_all([Key | Keys]) ->
    validate(Key),
    validate_all(Keys).

Related elvis configuration:

{elvis_style, variable_naming_convention, #{regex => "^[A-Z][a-zA-Z]([0-9a-zA-Z]+)$"}}
  • Use snake_case for naming applications and modules
%% Good
myapp/src/task_server.erl

Related elvis configuration:

{elvis_style, module_naming_convention, #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$"}}

Booleans

  • avoid and and or operators, prefer andalso and orelse, or in guards, where possible, combine expressions with , and ;
%% Bad
is_atom(Name) and Name =/= undefined.
is_binary(Task) or is_atom(Task).
%% Good
is_atom(Name) andalso Name =/= undefined.
is_binary(Task) orelse is_atom(Task).
%% Also good
when is_atom(Name), Name =/= undefined.
when is_binary(Task); is_atom(Task).

Formatting

The erlfmt formatter enforces the following rules automatically.

Whitespace

  • avoid trailing spaces

  • end each file with a newline

  • use single space after comma

%% Bad
application:get_env(kernel,start_pg2)
%% Good
application:get_env(kernel, start_pg2)
  • use single space before and after binary operator (for example: = , -, |, ||, !, ->, or =>)
%% Bad
Values=[
    #{key=>value},
    #rec{key=value,key2=value2},
    120-90,
    Server!Message
]
%% Good
Values = [
    #{key => value},
    #rec{key = value, key2 = value2},
    120 - 90,
    Server ! Message
]
  • do not use space after symbolic unary operators (for example: +, -)
%% Bad
Angle = - 45,
WithParens = - ( + 45 ).
%% Good
Angle = -45,
WithParens = -(+45).
  • do not put spaces before or after ( ), { }, or [] for function/record/maps/lists (declarations, definitions, calls)
%% Bad
function_definition( { ok, Argument } ) ->
    mod:function_call(Argument).

sort( [ 1,2,3 ] ).
%% Good
function_definition({ok, Argument}) ->
    mod:function_call(Argument).

sort([1, 2, 3]).
  • do not put spaces around segment definitions in binary patterns
%% Bad
<<102 : 8 / unsigned-big-integer, Rest / binary>>,
<<255/unsigned - big - integer, Rest/binary>>.
%% Good
<<102:8/unsigned-big-integer, Rest/binary>>,
<<255/unsigned-big-integer, Rest/binary>>.
  • avoid aligning expression groups
%% Bad
Module = Env#env.module,
Arity  = length(Args).

inspect(false) -> "false";
inspect(true)  -> "true";
inspect(ok)    -> "ok".
%% Good
Module = Env#env.module,
Arity = length(Args).

inspect(false) -> "false";
inspect(true) -> "true";
inspect(ok) -> "ok".

Indentation

  • use 4 spaces to indent, no hard tabs
  • inside of case, if, try, receive, and functions write all clauses on the same line or all clauses on separate lines, don't mix two clause styles.
%% Bad
lookup(1) -> one;
lookup(2) ->
    two.

reverse_lookup(one) ->
    1;
reverse_lookup(two) -> 2.
%% Good
lookup(1) -> one;
lookup(2) -> two.

reverse_lookup(one) ->
    1;
reverse_lookup(two) ->
    2.
  • always break comma-separated expressions across multiple lines:
%% Bad
run() -> compile(), execute().

case application:get_env(kernel, start_pg2) of
    {ok, true} -> pg2:start();
    undefined -> ?LOG_NOTICE("skipping pg2 start"), ok
end.

try Processed = process(Val), info(Processed)
catch
    throw:error -> error
end.
%% Good
run() ->
    compile(),
    execute().

case application:get_env(kernel, start_pg2) of
    {ok, true} ->
        pg2:start();
    undefined ->
        ?LOG_NOTICE("skipping pg2 start"),
        ok
end.

try
    Processed = process(Val),
    info(Processed)
catch
    throw:error -> error
end.
  • Indent the right-hand side of a binary operator if it is on a different line
%% Bad
f(Op, Atom) ->
    is_atom(Op) andalso
    Atom =/= '!' andalso
    Atom =/= '='.
%% Good
f(Op, Atom) ->
    is_atom(Op) andalso
        Atom =/= '!' andalso
        Atom =/= '='.
  • When assigning the result of a multi-line expression, begin the expression on a new line
%% Bad
Prefix = case Base of
             binary -> "2#";
             octal -> "8#";
             hex -> "16#"
         end.
%% Good
Prefix =
    case Base of
        binary -> "2#";
        octal -> "8#";
        hex -> "16#"
    end.

Exports

Every exported function occupies exactly 1 line, functions with same name, but different arity can be put on the same line, following the definition:

%% Good
-export([
    api_function/1,
    api_another/2, api_another/3,
    api_third/4
]).

Strings

  • do not use multilne strings
%% Bad
Message = "erlfmt is a code formatter for Erlang.

Arguments:

  files -- files to format
..."
%% Good
Message =
    "erlfmt is a code formatter for Erlang.\n"
    "\n"
    "Arguments:\n"
    "\n"
    "  files -- files to format\n"
    "..."
%% Also good
Message =
    "erlfmt is a code formatter for Erlang.\n\n"
    "Arguments:\n\n"
    "  files -- files to format\n"
    "..."

Line length

Line length must not exceed 100 characters.

Licence

This style guide was created by the maintainers of erlfmt and is licenced under the CC by 4.0 license and is partially based on the Elixir Style Guide.