Skills

Ecto Patterns

Use when a task touches Ecto, even a how-to question: schemas, changesets, validations...

Install

npx skills add oliver-kriska/claude-elixir-phoenix --skill ecto-patterns -g -a claude-code -y
Category
Writing
Updated
5d ago
License
MIT
Context cost
Lightabout 816 tokens
Runs scripts
No, instructions only

What it does

Use when a task touches Ecto, even a how-to question: schemas, changesets, validations, queries, preloads, Multi, migrations, constraints, money fields. Load it before writing Ecto code. Skip for Ash.

Ecto Patterns Reference

Reference for working with Ecto schemas, queries, and migrations.

Iron Laws, Never Violate These

  1. CHANGESETS ARE FOR EXTERNAL DATA, Use cast/4 for user/API input, change/2 or put_change/3 for internal trusted data
  2. NEVER USE :float FOR MONEY, Always use :decimal or :integer (cents)
  3. NO RAILS-STYLE POLYMORPHIC ASSOCIATIONS, They break foreign key constraints; use multiple nullable FKs or separate join tables
  4. ALWAYS PIN VALUES IN QUERIES, u.name == ^user_input is safe, string interpolation causes SQL injection
  5. PRELOAD COLLECTIONS, NOT INDIVIDUALS, Preloading in loops = N+1 queries
  6. CONSTRAINTS BEAT VALIDATIONS FOR RACE CONDITIONS, Validations provide quick feedback, constraints provide DB-level safety
  7. SEPARATE QUERIES FOR has_many, JOIN FOR belongs_to, Avoids row multiplication
  8. NO IMPLICIT CROSS JOINS, from(a in A, b in B) without on: creates Cartesian product
  9. DEDUP BEFORE cast_assoc WITH SHARED DATA, When multiple parents share child data, deduplicate child records BEFORE building changesets. Dedup only works within a single changeset

Quick Schema Template

defmodule MyApp.Context.Entity do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  schema "entities" do
    field :name, :string
    field :status, Ecto.Enum, values: [:draft, :active, :archived]
    field :amount_cents, :integer  # Never :float for money!
    belongs_to :user, MyApp.Accounts.User
    timestamps(type: :utc_datetime_usec)
  end

  def changeset(entity, attrs) do
    entity
    |> cast(attrs, [:name, :status, :amount_cents])
    |> validate_required([:name])
    |> foreign_key_constraint(:user_id)
  end
end

Quick Decisions

cast vs put_change vs change

Read the whole SKILL.md on GitHub

More in Writing