phoenix-thinkinglisted
Install: claude install-skill ahmedxx99/claude-code-elixir
# Phoenix Thinking
Mental shifts for Phoenix applications. These insights challenge typical web framework patterns.
## The Iron Law
```
NO DATABASE QUERIES IN MOUNT
```
mount/3 is called TWICE (HTTP request + WebSocket connection). Queries in mount = duplicate queries.
```elixir
def mount(_params, _session, socket) do
# NO database queries here! Called twice.
{:ok, assign(socket, posts: [], loading: true)}
end
def handle_params(params, _uri, socket) do
# Database queries here - once per navigation
posts = Blog.list_posts(socket.assigns.scope)
{:noreply, assign(socket, posts: posts, loading: false)}
end
```
**mount/3** = setup only (empty assigns, subscriptions, defaults)
**handle_params/3** = data loading (all database queries, URL-driven state)
**No exceptions:** Don't query "just this one small thing" in mount. Don't "optimize later". LiveView lifecycle is non-negotiable.
## Scopes: Security-First Pattern (Phoenix 1.8+)
Scopes address OWASP #1 vulnerability: Broken Access Control. Authorization context is threaded automatically—no more forgetting to scope queries.
```elixir
def list_posts(%Scope{user: user}) do
Post |> where(user_id: ^user.id) |> Repo.all()
end
```
## PubSub Topics Must Be Scoped
```elixir
def subscribe(%Scope{organization: org}) do
Phoenix.PubSub.subscribe(@pubsub, "posts:org:#{org.id}")
end
```
Unscoped topics = data leaks between tenants.
## External Polling: GenServer, Not LiveView
**Bad:** Every connected user makes API ca