---
name: Elixir / Phoenix coding conventions
description: Hard rules for Elixir/Phoenix code in this user's projects. Apply on every code change.
type: feedback
created: YYYY-MM-DD
---

**Why:** The Phoenix code is API-first, runs on AWS ECS, and prizes
readability and stdlib-first minimalism. These rules cut review time.

**How to apply:** On every Elixir code edit, check the change against this
list before saving.

## Hard rules

- **HTTP client: `Req`.** Never `:httpc`, `:tesla`, `:httpoison`, `:finch`
  directly. Req covers JSON, retries, timeouts, streaming.
- **No `else if`.** Use `cond` or `case`. (`else if` isn't even Elixir, but
  agents reach for it.)
- **List index access:** `Enum.at(list, i)`, never `list[i]` (will not compile
  in Elixir for non-Access types — and most lists aren't Access).
- **Rebind results of `if` / `case` / `cond` to a variable** at the outer
  scope. Do **not** rebind a variable inside one of those blocks — it won't
  leak out, and the bug is silent.

  ```elixir
  # good
  status = case result do
    {:ok, _}    -> :live
    {:error, _} -> :down
  end

  # bad — outer `status` never changes
  status = :unknown
  case result do
    {:ok, _}    -> status = :live
    {:error, _} -> status = :down
  end
  ```

- **Pattern-match in function heads** before reaching for `case` inside the
  body. Multiple clauses > nested case.
- **`with` for happy-path chaining** of `{:ok, _} | {:error, _}`. One `with`
  beats three nested cases.
- **Pipelines are vertical.** Each step on its own line. No four-step
  pipelines on one line.
- **No `defmacro` unless there is no other option.** Macros are last-resort.
- **No GenServer when a plain function will do.** Process state is overhead;
  reach for it only when you need long-lived state, supervision, or
  serialization.
- **Ecto queries: composable, not stringy.** Build `from`/`where` fragments;
  avoid `Repo.query` unless you need raw SQL for a reason you can articulate.
- **Tests: ExUnit only.** No alternative frameworks. Fixtures via `setup`,
  not factory packages.

## Production reality

- Phoenix services run `mix phx.server` in container (no Mix release yet).
- HTTP requests in tests use `Req.Test` stubs.
- Telemetry events are namespaced under the app's atom.
