- Define schemas with `schema` and `embedded_schema` macros — use `changeset/2` functions to validate and cast data before database operations.
- Use `Ecto.Multi` for complex transactions — chain operations and rollback all on failure: `Multi.new() |> Multi.insert(:user, changeset)`.
- Use `Repo.preload()` for associations and `Ecto.Query` composable queries: `from u in User, where: u.active == true, preload: [:posts]`.
- Define changesets in schema modules: `def changeset(struct, params)` with `cast` and `validate_*` functions.
- Use `Ecto.Query` with pipe syntax for composable queries: `from(u in User) |> where([u], u.active == true)`.
- Use `Repo.preload()` for eager loading associations. Avoid N+1 queries.
- Use `Ecto.Multi` for multi-step transactions with named operations.