towerops/policies/ELIXIR_CODE_STANDARDS.md
Graham McIntie 26c7b0b993 Hide RF Links UI elements from network map (TOW-44) (#46)
- Commented out 'View RF Links' button in network map node detail panel
- Commented out RF Link health legend (Good/Degraded/Critical) from map legend
- Backend code (topology.ex) left intact per instructions
- No RF tab found on device show page (nothing to hide there)
- All tests pass, compiles clean with --warnings-as-errors

Reviewed-on: graham/towerops-web#46
2026-03-16 15:19:03 -05:00

3 KiB

Elixir Code Standards Policy

Effective: 2026-03-15 Owner: Engineering

Purpose

All Elixir code in TowerOps must be idiomatic, readable, and maintainable. This policy ensures consistency across the codebase and reduces cognitive load for contributors.

Standards

Pattern Matching Over Conditionals

  • Use pattern matching in function heads instead of if/case when branching on structure.
  • Prefer multi-clause functions over nested conditionals.
# Good
def process(%{status: :active} = device), do: ...
def process(%{status: :inactive} = device), do: ...

# Avoid
def process(device) do
  if device.status == :active do
    ...
  else
    ...
  end
end

The Pipe Operator

  • Use |> for data transformation chains (3+ steps).
  • Never start a pipe with a raw value — assign or wrap it.
  • Each step in a pipe should take the piped value as its first argument.

With Clauses

  • Use with for sequences of dependent operations that may fail.
  • Always include an else clause that handles expected error shapes.
  • Keep with chains under 5 clauses; extract helpers if longer.

Naming

  • Modules: PascalCase, mirroring directory structure.
  • Functions: snake_case, verb-first (fetch_device/1, parse_response/1).
  • Private helpers: prefix with intent, not do_ (e.g., build_query not do_query).
  • Boolean functions: end with ? (e.g., active?/1).

Contexts (Phoenix)

  • Business logic lives in context modules (Towerops.Devices, Towerops.Alerts).
  • LiveViews and controllers call contexts — never Repo directly.
  • Contexts return {:ok, result} or {:error, reason} tuples.
  • Keep context functions focused; one public function per use case.

Error Handling

  • Use tagged tuples ({:ok, _} / {:error, _}) for expected failures.
  • Let unexpected failures crash — the supervisor will restart.
  • Never rescue broadly (rescue e ->) without re-raising or logging.
  • Use Ecto.Multi for operations that must be atomic.

Documentation

  • All public functions must have @doc and @spec.
  • Modules must have @moduledoc describing purpose and responsibilities.
  • Internal/private functions: document with comments only when non-obvious.

Testing

  • Every public context function must have tests.
  • Use ExUnit.CaseTemplate and factories for setup.
  • Async tests (async: true) unless they touch shared state (database).
  • Name tests descriptively: "fetch_device/1 returns error when device not found".

Formatting

  • Run mix format before every commit. CI will reject unformatted code.
  • Use .formatter.exs settings; do not override line length (98 chars).

Dependencies

  • Minimize external dependencies. Prefer stdlib and OTP.
  • Every new dependency requires a brief justification in the PR description.
  • Pin versions explicitly in mix.exs.

Enforcement

  • CI runs mix format --check-formatted, mix credo --strict, and mix dialyzer.
  • Code review required for all merges to main.
  • Violations block merge.