Best Practices
43.1 Design principles
Let it crash — handle expected errors with tagged tuples; let unexpected ones crash and be restarted by supervisors.
Isolate failures with processes; one process per concurrent activity (connection, session, job).
Keep the error kernel small. Assertive code: match on expected results ({ok, X} =...) instead of ignoring errors.
Separate API from implementation: export client functions that wrap gen_server:call/cast — never send raw messages to OTP processes from outside.
Pure core, impure shell: put logic in pure functions, test them independently.
Design supervision trees first — think about start order and failure domains.
Avoid unbounded growth: mailboxes, ETS tables, atom creation.
Use OTP behaviours instead of raw spawn for long-lived processes.
Name things consistently: app_sup, app_server, app_worker.
43.2 Code style
Follow the Inaka / WhatsApp Erlang style guides; format with erlfmt (rebar3 fmt).
Lint with elvis (rebar3 lint).
Run xref and dialyzer in CI; compile with warnings_as_errors.
4-space indentation; lines under ~100 characters.
Write -spec for all exported functions.
Avoid -compile(export_all) and -import in production code.
Prefer maps and records over long tuples; tag tuples with atoms.
Avoid deep nesting — extract helper functions.
43.3 Common pitfalls
Atom leaks from list_to_atom/1 flow control, {active, once}, catch-all receive clause gen_server bottleneck shard, use ETS for reads, offload to workers Sub-binary memory leaks Long NIF calls dirty schedulers, yielding start under supervisors catch-all hiding bugs catch specific errors only Blocking calls in init/1 {continue,...} / handle_continue/2 Mismatched cookie / EPMD firewall check vm.args, open 4369 + dist ports Using == when =:= is meant prefer exact comparison
43.4 Security
Never binary_to_term/1 untrusted data — use binary_to_term(B, [safe]) or a safer format (JSON).
Distribution grants full remote code execution to anyone with the cookie — firewall EPMD and dist ports; use TLS distribution.
Validate TLS certificates ({verify, verify_peer}, public_key:cacerts_get()).
Avoid os:cmd/1 with user input (shell injection) — use open_port({spawn_executable, Path}, [{args, Args}]).
Keep OTP updated (security patches, e.g., SSH vulnerabilities).
Follow the EEF Security WG secure coding guidelines.