- 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
93 lines
3 KiB
Markdown
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.
|