What Changed
Released v2.1.0 of my Elixir Phoenix Guide plugin. Six new skills bring the total to 14. One new hook brings it to 15. The plugin now covers the full Phoenix development lifecycle, from authentication through data patterns to deployment safety.
From Detection to Coverage
v2.0.0 introduced automated code quality detection. That was about depth: analyzing what you just wrote and surfacing problems. v2.1.0 is about breadth. The plugin can now guide Claude Code through authentication flows, authorization logic, complex changeset patterns, real-time PubSub, nested associations, and safe migrations.
Each of these skills came from a real debugging session. The kind where you spend 30 to 90 minutes tracking down why cast_assoc keeps returning “can’t be blank” on a foreign key that should be set automatically, or why @current_scope crashes in a template when the user isn’t logged in. These are patterns that look straightforward in the docs but break in specific, frustrating ways in practice.
What’s New in v2.1.0
6 New Skills
| Skill | Rules | Covers |
|---|---|---|
| phoenix-liveview-auth | 7 | on_mount callbacks, current_scope, import conflicts, session handling |
| ecto-changeset-patterns | 7 | Separate changesets per operation, cast_assoc pitfalls, composition |
| phoenix-auth-customization | 6 | Extending phx.gen.auth with custom fields, fixtures, confirmation |
| phoenix-pubsub-patterns | 6 | Subscriptions, broadcasting from contexts, topic naming |
| phoenix-authorization-patterns | 6 | Server-side authz, ownership, policy modules, scoped queries |
| ecto-nested-associations | 6 | cast_assoc, Ecto.Multi, cascades, FK indexes |
phoenix-liveview-auth
LiveView authentication has a few patterns that look simple until you hit the edge cases. The skill enforces on_mount callbacks over inline auth checks in mount/3, and catches the bracket-access issue that bites most people at least once.
The standard pattern:
# Define once in UserAuth
def on_mount(:require_authenticated_user, _params, session, socket) do
socket = mount_current_scope(socket, session)
if socket.assigns.current_scope && socket.assigns.current_scope.user do
{:cont, socket}
else
socket =
socket
|> put_flash(:error, "You must log in to access this page.")
|> redirect(to: ~p"/users/log_in")
{:halt, socket}
end
end
One rule that saves a lot of confusion: use assigns[:current_scope] in templates, not @current_scope. Dot access crashes on nil when the user isn’t authenticated. Bracket access returns nil. Small difference, but it matters in layouts rendered for both authenticated and unauthenticated users.
Another: Phoenix.Controller and Phoenix.LiveView both export redirect/2 and put_flash/3. The skill enforces explicit except: imports to avoid ambiguity.
ecto-changeset-patterns
The biggest rule here is separate changesets per operation. Registration needs email, username, and password validation. Email changes need only email validation with reconfirmation. Password changes need only password validation. Overloading a single changeset/2 with conditional logic leads to bugs.
# Registration — all fields, password hashing
def registration_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:email, :username, :password])
|> validate_email(opts)
|> validate_username()
|> validate_password(opts)
end
# Email change — only email
def email_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:email])
|> validate_email(opts)
end
# Profile update — non-sensitive fields only
def profile_changeset(user, attrs) do
user
|> cast(attrs, [:username, :bio])
|> validate_username()
end
The cast_assoc rule is the one that saves the most time. When you use cast_assoc, the parent sets the foreign key automatically. If your child changeset requires that foreign key in cast, you get “can’t be blank” errors that make no sense until you realize the key hasn’t been set yet. The skill catches this before you waste 30 minutes on it.
migration-safety Hook
The 15th hook in the plugin. It runs before any migration file is written and checks for four common deployment issues:
- Missing FK indexes on
references()columns - Missing
on_deletestrategy (should you cascade, nilify, or do nothing?) - Unsafe column removals without a safety comment explaining the two-step deploy process
NOT NULLwithout a default (locks the table on large datasets)
⚠️ Migration Safety Check:
- Missing index on foreign key column(s). Add: create index(:table, [:foreign_key_id])
- Missing on_delete strategy on references(). Specify on_delete: :delete_all, :nothing, or :nilify_all
These are the kind of issues that pass locally, pass in CI, and break in production on a table with a few million rows. The hook catches them before the migration is committed.
SubagentStart Hook Update
Every time Claude Code spawns a subagent, it now injects condensed rules from all 14 skills. This means code written by subagents follows the same patterns as the main conversation. The auth rules, changeset patterns, and authorization checks all carry through.
The Full Picture
| Component | v1.4.0 | v2.0.0 | v2.1.0 |
|---|---|---|---|
| Skills | 7 | 8 | 14 |
| Hooks | 13 | 14 | 15 |
| Analysis Scripts | 0 | 3 | 3 |
| Agent Docs | 4 | 4 | 4 |
The plugin started with Elixir fundamentals, LiveView, and Ecto. v1.4.0 added OTP and Oban. v2.0.0 added automated code quality detection. v2.1.0 fills the remaining gaps: authentication, authorization, advanced data patterns, and migration safety. If you’re building a Phoenix app with Claude Code, the plugin now has guidance for every layer of the stack.
Update
/plugin
# Select "Marketplaces" → "elixir-phoenix-guide" → "Update"
Try It
GitHub: elixir-phoenix-guide
Requirements: Elixir 1.15+, Phoenix 1.7+