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

93 lines
3 KiB
Markdown

# 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.
```elixir
# 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.