deployment-gotchas
Use when preparing releases or deployment config — mix phx.gen.release, runtime.exs vs compile-time config, release migrations,…
Use when refactoring or reviewing Elixir code quality — compiler warnings and Credo first, then the plugin's on-demand duplication and branch-complexity scans. Not for style rules while writing code (use elixir-essentials).
$ npx -y skills add j-morgan6/elixir-phoenix-guide --skill code-quality --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/code-qualityContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when refactoring or reviewing Elixir code quality — compiler warnings and Credo first, then the plugin's on-demand duplication and branch-complexity scans. Not for style rules while writing code (use elixir-essentials).
name: code-quality description: "Use when refactoring or reviewing Elixir code quality — compiler warnings and Credo first, then the plugin's on-demand duplication and branch-complexity scans. Not for style rules while writing code (use elixir-essentials)."
> Verified against Elixir 1.20, Phoenix 1.8, LiveView 1.2, Credo 1.7 — October 2026
On-demand checks for duplication and complexity. Nothing here runs automatically: the plugin's hooks check security and deprecated-API patterns on the lines Claude writes, not duplication, complexity or templates.
1. **Run `mix compile --warnings-as-errors` first** — the compiler reports unused private functions, variables, aliases and imports exactly; fix those before anything else 2. **Then run `mix credo --strict`** when the project has Credo — its `.credo.exs` thresholds override the numbers in this skill 3. **Extract duplicated function bodies** — when 2+ functions share a body that differs only in names or literals, keep one function and pass the differences as arguments 4. **Keep branch complexity low** — split functions the analyzer flags (above 15) or Credo flags (`CyclomaticComplexity`, default max 9) into pattern-matched clauses or helpers 5. **Take a baseline before a large refactor** — run the analyzer before and after, and compare 6. **Address duplication before complexity** — extracting shared code often removes branches too 7. **Share code through plain modules** — callers `alias` or `import` the shared module; don't build `__using__` base modules just to share functions 8. **Repeated HEEx markup becomes a function component** — a manual guideline; no tool checks templates
---
1. `mix compile --warnings-as-errors` — unused code (the plugin no longer has its own unused-function check; the compiler is exact) 2. `mix credo --strict` — the standard style and complexity checks. `Refactor.CyclomaticComplexity` is on by default (max 9); `Design.DuplicatedCode` and `Refactor.ABCSize` are opt-in in Credo's default config 3. `mix xref callers MyApp.Mod.fun/2` and `mix xref graph` — who uses a function or module across the project 4. The plugin's analyzer (below) — duplication with renamed variables and changed literals across the whole tree, and a branch-complexity count for projects without Credo
The scripts ship with the plugin. They need only `elixir` (no deps, no compilation) and exit 1 when they find something. `${CLAUDE_SKILL_DIR}` is this skill's own directory; Claude Code fills it in, and in other agents it means the folder containing this file.
# Whole project
elixir "${CLAUDE_SKILL_DIR}/../../scripts/code_quality.exs" scan lib/
# Umbrella: scan all apps in one pass so duplication across apps is found
elixir "${CLAUDE_SKILL_DIR}/../../scripts/code_quality.exs" scan apps/*/lib
# One file: complexity of that file, duplication against its whole lib/ tree
elixir "${CLAUDE_SKILL_DIR}/../../scripts/code_quality.exs" all lib/my_app_web/live/post_live/index.ex
# One check only
elixir "${CLAUDE_SKILL_DIR}/../../scripts/code_quality.exs" duplication lib/
elixir "${CLAUDE_SKILL_DIR}/../../scripts/code_quality.exs" complexity lib/my_app/reports.ex
# Wrapper: picks lib/ or apps/*/lib and reminds you of compile/credo first
bash "${CLAUDE_SKILL_DIR}/../../scripts/run_analysis.sh" .---
Two function bodies count as duplicates when their ASTs are identical after variable names are replaced and string/number literals are erased. Function names, arity, module and directory don't matter. Atoms, calls and structure must match. Bodies under 25 AST nodes are ignored.
**Example output** (from a fresh `mix phx.gen.auth` app):
Duplicated function bodies (identical after renaming variables and literals) deliver_update_email_instructions/2 lib/demo/accounts/user_notifier.ex:24 deliver_magic_link_instructions/2 lib/demo/accounts/user_notifier.ex:51 deliver_confirmation_instructions/2 lib/demo/accounts/user_notifier.ex:68 Suggestion: extract one shared function (parameterize what differs).
Generated code like this is often fine to leave as it is. In your own code, extract the shared body:
defmodule MyApp.Reports.Summary do
@moduledoc "Totals shared by the daily and weekly reports."
def summarize(rows, label, precision) do
{count, sum} = Enum.reduce(rows, {0, 0}, fn row, {c, s} -> {c + 1, s + row.amount} end)
average = if count > 0, do: sum / count, else: 0
%{total: sum, average: Float.round(average * 1.0, precision), label: label}
end
end
# Callers:
# MyApp.Reports.Summary.summarize(rows, "daily", 2)
# MyApp.Reports.Summary.summarize(entries, "weekly", 3)**Limits:** only exact matches after normalization are found, so a copy with one added line is missed (Credo's opt-in `DuplicatedCode` check with a `mass_threshold` catches near-copies). Each clause of a multi-clause function is compared on its own. Only `.ex` files are scanned.
Branch complexity is 1, plus 1 for each `if`/`unless`, each `->` clause (in `case`, `cond`, `receive`, `fn` and `with ... else`), each `with`, and each `&&`, `||`, `and`, `or`. It approximates cyclomatic complexity. The threshold is 15. Credo's `Refactor.CyclomaticComplexity` is the standard check, and the project's `.credo.exs` limit takes precedence.
**Example output:**
High branch complexity (threshold 15) classify/3 lib/my_app/classifier.ex:2 branch complexity 16 Suggestion: split into smaller functions or pattern-matched clauses.
**How to fix:** move each decision into a function head.
defmodule MyApp.Classifier do @moduledoc "Classifies pairs of numbers." def classify(a, b, _c) when a > 10 and b > 10, do: :both_big def classify(a, b, _c) when a > 10 or b > 10, do: :one_big def classify(a, a, :x), do: :same def classify(a, _b, c) when c == :y or a < 0, do: :
· Changelog A Claude Code plugin (with Codex support for the skills) that keeps Claude's Elixir and Phoenix code idiomatic and current.
Use when preparing releases or deployment config — mix phx.gen.release, runtime.exs vs compile-time config, release migrations,…
Use when writing Ecto changeset validations beyond cast/validate_required — custom and cross-field validations (e.g. end date…
Use when defining schemas, writing queries, or creating migrations — scoped context functions, Repo.transact, upserts, indexes,…
Use when migrating PostgreSQL tables that serve live traffic — index concurrently,…
Use when one form or operation manages parent and child records together — cast_assoc/cast_embed, inputs_for with sort/drop…
Use when a query is slow, N+1 or hard to compose — preload vs join-preload, dynamic/2, subquery/exists, Repo.aggregate,…