- Use `snake_case` for module names, function names, and variables that start with an uppercase letter are atoms — prefix unbound variables with `_` to suppress warnings.
- Export only the public API from each module using `-export([function/arity, ...])` — keep internal helpers unexported.
- Prefer pattern matching over `if`/`case` guards where possible — use multiple function clauses with patterns to dispatch on different input shapes.
- Use `-spec` type annotations for all exported functions to enable `dialyzer` static analysis.
- Write short modules with a single responsibility — `gen_server` callbacks, helper functions, and types belong in separate modules.
- Use atoms for tags and status values (`:ok`, `:error`, `:ignore`) — they are interned and compared in O(1).
- Use `?MODULE` macro in `start_link` calls and `gen_server:call(?MODULE, ...)` to avoid repeating the module name as a string.
- Structure modules with a consistent layout: `-module`, `-behaviour`, `-export`, `-record`, `-type`, `-spec`, function definitions.