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.