Elixir Knowledge Patch
Use this index to load only the references relevant to the task. Apply migration
notes before adopting newer APIs, especially when upgrading an existing runtime,
application, or framework dependency.
Reference index
| Reference |
Topics |
| ecto.md |
Ecto queries, schemas, changesets, repositories, adapter migrations |
| elixir-language-and-core.md |
Elixir syntax, core APIs, JSON, files, regexes, processes, compatibility |
| erlang-otp.md |
Erlang syntax and libraries, processes, tracing, profiling, storage, security |
| interop-and-portability.md |
Browser Elixir, C++, Zig, Python, and Swift interoperability |
| phoenix-and-liveview.md |
Phoenix generators and scopes, layouts, authentication, LiveView components and tests |
| tooling-testing-and-releases.md |
Mix, compiler behavior, formatter, IEx, ExUnit, ExDoc, release artifacts |
| types-and-static-analysis.md |
Set-theoretic types, inference, diagnostics, Dialyzer nominal types |
Breaking changes and required migrations
Check runtime compatibility
- Run Elixir 1.20 on Erlang/OTP 27 or newer; it is compatible with OTP 29.
- Run Phoenix 1.8 on OTP 25 or newer; when upgrading, install its matching generator with
mix archive.install hex phx_new 1.8.0 --force.
- Treat Elixir 1.18 as the last release supporting OTP 25. On Windows, use OTP
26 or newer; WERL is unsupported.
Update source constructs
- Split
require(SomeModule).some_macro() into require SomeModule followed by SomeModule.some_macro(); require/1 no longer expands to module AST.
- Pin an already-bound bitstring size:
<<value::size(^size)>>.
- Explicitly match a struct before updating it:
def set_path(%URI{} = uri), do: %{uri | path: "/"}.
- Remove recursive pattern-variable cycles and express equality in guards.
- Separate scripts in identifiers with underscores. Direct mixed-script
identifiers are rejected, and bidirectional confusables warn.
- Give descending
Range.new/3 calls an explicit negative step.
- Remove raw carriage returns and U+2028/U+2029 line breaks from affected source
strings and comments.
Migrate deprecated APIs and options
- Call
File.stream!(path, lines_or_bytes, modes) in that order.
- Replace
Logger.enable/1 and Logger.disable/1 with Logger.put_process_level/2 and Logger.delete_process_level/1.
- Replace Logger
:backends configuration by disabling :default_handler or
starting custom backends from the application callback.
- Move
xref: [exclude: ...] to elixirc_options: [no_warn_undefined: ...].
- Move
:default_task, :preferred_cli_env, and :preferred_cli_target from
project/0 to cli/0 as :default_task, :preferred_envs, and
:preferred_targets.
- Join
mix do tasks with +, rename --no-protocol-consolidation to --no-consolidate-protocols, and stop invoking inert mix compile.protocols.
- Pass
--warnings-as-errors to mix compile or mix test; do not set :warnings_as_errors through compiler options.
- Use
mix do --app APP instead of mix cmd --app APP. mix cmd now preserves
quoting and skips shell expansion unless --shell precedes the command.
- Replace
List.zip/1, Module.eval_quoted/3, Tuple.append/2, and
Mix.Tasks.Compile.compilers/0 with Enum.zip/1, Code.eval_quoted/3,
Tuple.insert_at/3, and Mix.Task.Compiler.compilers/0.
- Write EEx comments as
<%!-- ... --%> or <% # ... %>, and implement EEx.handle_text/3 rather than arity two.
- Replace protocol
Any.__deriving__/3 callbacks with a protocol-owned optional
__deriving__/1 macro.
Account for compiler and regex behavior
- Do not assume project modules load immediately during compilation. Use
Kernel.ParallelCompiler.pmap/2 or Code.ensure_compiled!/1 before spawning compiler-time work.
- Pass
return_diagnostics: true to Kernel.ParallelCompiler.compile, compile_to_path, and require.
- Do not define a struct or exception inside
defprotocol.
- Initialize regex struct fields at construction time on OTP 28 rather than
using compiled regexes as struct defaults.
- Recompile regexes per node and runtime version. OTP's PCRE2-backed
re parser rejects some formerly tolerated escapes, and compiled representations are not portable.
- Replace
Inspect.Algebra.next_break_fits with optimistic or pessimistic
groups.
- Remove
on_undefined_variable: :warn; undefined identifiers no longer fall
back to function calls.
Update Ecto integrations
- Make adapters handle
distinct, group_by, order_by, and window as Ecto.Query.ByExpr, not QueryExpr.
- Initialize parameterized types with
Ecto.ParameterizedType.init/2; do not
depend on their changed private tuple representation.
- Remove the deleted
:array_join join type.
- Use
allow_stale: true only when intentionally accepting a stale struct or
changeset write.
Upgrade LiveView wiring and tests
- Put
:phoenix_live_view before standard Mix compilers, add LazyHTML for tests, and remove Floki only if no other dependency uses it.
- For colocated code, update esbuild, add
--alias:@=., and configure
NODE_PATH for dependency and build paths.
- Rename old global hook names beginning with
.; leading-dot colocated hooks
are now module-prefixed.
- Replace Floki-only
fl-contains and fl-icontains selectors with LiveViewTest
text filters.
- Fix duplicate DOM and LiveComponent IDs;
live/3 and live_isolated/3 raise for duplicates by default.
- Add
annotate_slot/4 to custom Phoenix.LiveView.TagEngine
implementations.
Core language quick reference
Use built-in JSON
Encode and decode with JSON; object keys decode as binaries. Derive selected
struct fields through JSON.Encoder:
defmodule User do
@derive {JSON.Encoder, only: [:id, :name]}
defstruct [:id, :name, :email]
end
json = JSON.encode!(%User{id: 1, name: "Ada"})
%{"id" => 1, "name" => "Ada"} = JSON.decode!(json)
Calendar types already implement the protocol. In Erlang, the json module
also decodes object keys as binaries by default.
Read type warnings structurally
- Expect inference across guards, anonymous functions, protocols, calls, returns, and all other language constructs.
- Read
dynamic(t) as dynamic() and t, not as an unconstrained escape hatch.
- Write open maps with leading
..., optional fields with if_set(type),
forbidden fields with not_set(), and open tuples with trailing ....
- Remember that later clauses exclude inputs definitely accepted earlier.
- During local inference, another module in the same project is
dynamic(); whole-project checking still compares modules afterward.
- Guard a comprehension with an explicit non-empty check when one-iteration
inference creates a false positive.
Use inferred map operations
Map.put(map, :key, 123) # key becomes required
Map.delete(map, :key) # key becomes forbidden
Map.replace(map, :key, 123) # key remains optional
Bang operations propagate required-key information and reveal calls statically
known to fail.
Reach for current core APIs
- Normalize calendar-style durations with
Kernel.to_timeout/1.
- Use
File.read(path, [:raw]) for raw reads. File.cp_r/3 skips special files, preserves directory permissions, and avoids symlink and nested-destination loops.
- Import uppercase
/E regular expressions with Regex.import/1; use
Regex.to_embed/2 when embedding one regex in another.
- Use
min/2 and max/2 in guards.
- Pass
{:via, module, term} names to PartitionSupervisor.count_children/1 and stop/3.
- Customize embedded
dbg evaluation with :dbg_callback; pipeline debugging
prints every intermediate stage.
Testing, compilation, and framework quick reference
Structure concurrent ExUnit suites
use ExUnit.Case,
async: true,
group: :postgres,
parameterize: [%{partitions: 1}, %{partitions: 8}]
Read parameter values from the test context. It also includes :test_pid and
:test_group. Doctests support exception-tail ellipses and :inspect_opts.
Reach for current Mix and IEx commands
mix source Enum.map/2
mix format --no-compile
mix test --dry-run
mix test --name-pattern PATTERN
mix xref graph --format json
Use MIX_OS_DEPS_COMPILE_PARTITION_COUNT to compile dependencies across OS
processes, balancing speed against memory. Use
ERL_COMPILER_OPTIONS=deterministic only when stripped source and compile
metadata are acceptable.
Compose Ecto queries and schemas
- Use subqueries in by-expressions, literal maps in
dynamic/2, dynamic values in selected map updates, and any Enumerable on the right of query in.
- Let root
order_by macros expand to the full expression and preload subquery
sources.
- Use arity-two custom preload functions to receive parent IDs and association
metadata.
- Supply source-only or update-syntax queries to
Repo.insert_all/3; use the
broader select_merge support for distinct fields.
- Mark read-only fields with
writable: :never, default embeds_one values
with defaults_to_struct: true, and store durations with :duration.
Build LiveView interfaces
- Define colocated hooks with
Phoenix.LiveView.ColocatedHook and arbitrary colocated JavaScript with Phoenix.LiveView.ColocatedJS; merge generated hooks into the LiveSocket configuration.
- Add
:key to comprehensions when identity must survive insertion or
reordering; prefer streams for very large collections.
- Render elsewhere in the DOM with
Phoenix.Component.portal/1 while retaining LiveView event ownership.
- Preserve browser-controlled attributes with
JS.ignore_attributes/1.
- Use
stream_insert(..., update_only: true) to update without inserting.
- Enable
debug_heex_annotations and debug_attributes for definition,
caller, slot, line, and LiveView PID annotations.
Follow Phoenix-generated boundaries
- Expect magic-link authentication by default and use generated
require_sudo_mode for recently authenticated operations.
- Pass the generated application-owned scope through contexts, queries, foreign
keys, PubSub topics, and authenticated LiveView sessions.
- Call app layout function components explicitly so each layout can accept its
own assigns and slots.
- Treat Tailwind v4, daisyUI, themes, and the layout theme toggle as generator
defaults, not requirements of
phx.gen.* output.
Erlang/OTP quick reference
- Send priority messages only through a priority alias and the
priority send option; prioritize exit, link, and monitor signals through their APIs.
- Use strict comprehension generators (
<:-, <:=) when non-matches must
fail, and zip generators with && for parallel iteration.
- Treat native records and comprehension assignment as experimental features.
- Prefer immutable
graph when persistent graph versions are useful.
- Cap tar extraction with
{max_size, Size}.
- Explicitly enable required SSH shell, exec, and SFTP services. SSL and SSH
prefer hybrid ML-KEM-768/X25519 and fall back for older peers.
- Use
proc_lib labels, independent trace sessions, unified tprof, and
native coverage to diagnose runtime behavior.
Interoperability selection
- Use Popcorn for an AtomVM WebAssembly subset in the browser or Hologram for
Phoenix-based isomorphic components transpiled to JavaScript.
- Use Fine for signature-driven C++ NIFs or Zigler for inline Zig compiled at
build time.
- Use Pythonx for in-process Python with
uv-managed dependencies; account for
GIL serialization unless native packages release it.
- Use the Swift Erlang Actor System when a Swift program must participate as a
distributed node.
1---2name: elixir-knowledge-patch-23description: Elixir4license: MIT5---678# Elixir Knowledge Patch910Use this index to load only the references relevant to the task. Apply migration11notes before adopting newer APIs, especially when upgrading an existing runtime,12application, or framework dependency.1314## Reference index1516| Reference | Topics |17|---|---|18| [ecto.md](references/ecto.md) | Ecto queries, schemas, changesets, repositories, adapter migrations |19| [elixir-language-and-core.md](references/elixir-language-and-core.md) | Elixir syntax, core APIs, JSON, files, regexes, processes, compatibility |20| [erlang-otp.md](references/erlang-otp.md) | Erlang syntax and libraries, processes, tracing, profiling, storage, security |21| [interop-and-portability.md](references/interop-and-portability.md) | Browser Elixir, C++, Zig, Python, and Swift interoperability |22| [phoenix-and-liveview.md](references/phoenix-and-liveview.md) | Phoenix generators and scopes, layouts, authentication, LiveView components and tests |23| [tooling-testing-and-releases.md](references/tooling-testing-and-releases.md) | Mix, compiler behavior, formatter, IEx, ExUnit, ExDoc, release artifacts |24| [types-and-static-analysis.md](references/types-and-static-analysis.md) | Set-theoretic types, inference, diagnostics, Dialyzer nominal types |2526## Breaking changes and required migrations2728### Check runtime compatibility2930- Run Elixir 1.20 on Erlang/OTP 27 or newer; it is compatible with OTP 29.31- Run Phoenix 1.8 on OTP 25 or newer; when upgrading, install its matching generator with `mix archive.install hex phx_new 1.8.0 --force`.32- Treat Elixir 1.18 as the last release supporting OTP 25. On Windows, use OTP33 26 or newer; WERL is unsupported.3435### Update source constructs3637- Split `require(SomeModule).some_macro()` into `require SomeModule` followed by `SomeModule.some_macro()`; `require/1` no longer expands to module AST.38- Pin an already-bound bitstring size: `<<value::size(^size)>>`.39- Explicitly match a struct before updating it:40 `def set_path(%URI{} = uri), do: %{uri | path: "/"}`.41- Remove recursive pattern-variable cycles and express equality in guards.42- Separate scripts in identifiers with underscores. Direct mixed-script43 identifiers are rejected, and bidirectional confusables warn.44- Give descending `Range.new/3` calls an explicit negative step.45- Remove raw carriage returns and U+2028/U+2029 line breaks from affected source46 strings and comments.4748### Migrate deprecated APIs and options4950- Call `File.stream!(path, lines_or_bytes, modes)` in that order.51- Replace `Logger.enable/1` and `Logger.disable/1` with `Logger.put_process_level/2` and `Logger.delete_process_level/1`.52- Replace Logger `:backends` configuration by disabling `:default_handler` or53 starting custom backends from the application callback.54- Move `xref: [exclude: ...]` to `elixirc_options: [no_warn_undefined: ...]`.55- Move `:default_task`, `:preferred_cli_env`, and `:preferred_cli_target` from56 `project/0` to `cli/0` as `:default_task`, `:preferred_envs`, and57 `:preferred_targets`.58- Join `mix do` tasks with `+`, rename `--no-protocol-consolidation` to `--no-consolidate-protocols`, and stop invoking inert `mix compile.protocols`.59- Pass `--warnings-as-errors` to `mix compile` or `mix test`; do not set `:warnings_as_errors` through compiler options.60- Use `mix do --app APP` instead of `mix cmd --app APP`. `mix cmd` now preserves61 quoting and skips shell expansion unless `--shell` precedes the command.62- Replace `List.zip/1`, `Module.eval_quoted/3`, `Tuple.append/2`, and63 `Mix.Tasks.Compile.compilers/0` with `Enum.zip/1`, `Code.eval_quoted/3`,64 `Tuple.insert_at/3`, and `Mix.Task.Compiler.compilers/0`.65- Write EEx comments as `<%!-- ... --%>` or `<% # ... %>`, and implement `EEx.handle_text/3` rather than arity two.66- Replace protocol `Any.__deriving__/3` callbacks with a protocol-owned optional67 `__deriving__/1` macro.6869### Account for compiler and regex behavior7071- Do not assume project modules load immediately during compilation. Use `Kernel.ParallelCompiler.pmap/2` or `Code.ensure_compiled!/1` before spawning compiler-time work.72- Pass `return_diagnostics: true` to `Kernel.ParallelCompiler.compile`, `compile_to_path`, and `require`.73- Do not define a struct or exception inside `defprotocol`.74- Initialize regex struct fields at construction time on OTP 28 rather than75 using compiled regexes as struct defaults.76- Recompile regexes per node and runtime version. OTP's PCRE2-backed `re` parser rejects some formerly tolerated escapes, and compiled representations are not portable.77- Replace `Inspect.Algebra.next_break_fits` with optimistic or pessimistic78 groups.79- Remove `on_undefined_variable: :warn`; undefined identifiers no longer fall80 back to function calls.8182### Update Ecto integrations8384- Make adapters handle `distinct`, `group_by`, `order_by`, and `window` as `Ecto.Query.ByExpr`, not `QueryExpr`.85- Initialize parameterized types with `Ecto.ParameterizedType.init/2`; do not86 depend on their changed private tuple representation.87- Remove the deleted `:array_join` join type.88- Use `allow_stale: true` only when intentionally accepting a stale struct or89 changeset write.9091### Upgrade LiveView wiring and tests9293- Put `:phoenix_live_view` before standard Mix compilers, add LazyHTML for tests, and remove Floki only if no other dependency uses it.94- For colocated code, update esbuild, add `--alias:@=.`, and configure95 `NODE_PATH` for dependency and build paths.96- Rename old global hook names beginning with `.`; leading-dot colocated hooks97 are now module-prefixed.98- Replace Floki-only `fl-contains` and `fl-icontains` selectors with LiveViewTest99 text filters.100- Fix duplicate DOM and LiveComponent IDs; `live/3` and `live_isolated/3` raise for duplicates by default.101- Add `annotate_slot/4` to custom `Phoenix.LiveView.TagEngine`102 implementations.103104## Core language quick reference105106### Use built-in JSON107108Encode and decode with `JSON`; object keys decode as binaries. Derive selected109struct fields through `JSON.Encoder`:110111```elixir112defmodule User do113 @derive {JSON.Encoder, only: [:id, :name]}114 defstruct [:id, :name, :email]115end116117json = JSON.encode!(%User{id: 1, name: "Ada"})118%{"id" => 1, "name" => "Ada"} = JSON.decode!(json)119```120121Calendar types already implement the protocol. In Erlang, the `json` module122also decodes object keys as binaries by default.123124### Read type warnings structurally125126- Expect inference across guards, anonymous functions, protocols, calls, returns, and all other language constructs.127- Read `dynamic(t)` as `dynamic() and t`, not as an unconstrained escape hatch.128- Write open maps with leading `...`, optional fields with `if_set(type)`,129 forbidden fields with `not_set()`, and open tuples with trailing `...`.130- Remember that later clauses exclude inputs definitely accepted earlier.131- During local inference, another module in the same project is `dynamic()`; whole-project checking still compares modules afterward.132- Guard a comprehension with an explicit non-empty check when one-iteration133 inference creates a false positive.134135### Use inferred map operations136137```elixir138Map.put(map, :key, 123) # key becomes required139Map.delete(map, :key) # key becomes forbidden140Map.replace(map, :key, 123) # key remains optional141```142143Bang operations propagate required-key information and reveal calls statically144known to fail.145146### Reach for current core APIs147148- Normalize calendar-style durations with `Kernel.to_timeout/1`.149- Use `File.read(path, [:raw])` for raw reads. `File.cp_r/3` skips special files, preserves directory permissions, and avoids symlink and nested-destination loops.150- Import uppercase `/E` regular expressions with `Regex.import/1`; use151 `Regex.to_embed/2` when embedding one regex in another.152- Use `min/2` and `max/2` in guards.153- Pass `{:via, module, term}` names to `PartitionSupervisor.count_children/1` and `stop/3`.154- Customize embedded `dbg` evaluation with `:dbg_callback`; pipeline debugging155 prints every intermediate stage.156157## Testing, compilation, and framework quick reference158159### Structure concurrent ExUnit suites160161```elixir162use ExUnit.Case,163 async: true,164 group: :postgres,165 parameterize: [%{partitions: 1}, %{partitions: 8}]166```167168Read parameter values from the test context. It also includes `:test_pid` and169`:test_group`. Doctests support exception-tail ellipses and `:inspect_opts`.170171### Reach for current Mix and IEx commands172173```console174mix source Enum.map/2175mix format --no-compile176mix test --dry-run177mix test --name-pattern PATTERN178mix xref graph --format json179```180181Use `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` to compile dependencies across OS182processes, balancing speed against memory. Use183`ERL_COMPILER_OPTIONS=deterministic` only when stripped source and compile184metadata are acceptable.185186### Compose Ecto queries and schemas187188- Use subqueries in by-expressions, literal maps in `dynamic/2`, dynamic values in selected map updates, and any `Enumerable` on the right of query `in`.189- Let root `order_by` macros expand to the full expression and preload subquery190 sources.191- Use arity-two custom preload functions to receive parent IDs and association192 metadata.193- Supply source-only or update-syntax queries to `Repo.insert_all/3`; use the194 broader `select_merge` support for distinct fields.195- Mark read-only fields with `writable: :never`, default `embeds_one` values196 with `defaults_to_struct: true`, and store durations with `:duration`.197198### Build LiveView interfaces199200- Define colocated hooks with `Phoenix.LiveView.ColocatedHook` and arbitrary colocated JavaScript with `Phoenix.LiveView.ColocatedJS`; merge generated hooks into the `LiveSocket` configuration.201- Add `:key` to comprehensions when identity must survive insertion or202 reordering; prefer streams for very large collections.203- Render elsewhere in the DOM with `Phoenix.Component.portal/1` while retaining LiveView event ownership.204- Preserve browser-controlled attributes with `JS.ignore_attributes/1`.205- Use `stream_insert(..., update_only: true)` to update without inserting.206- Enable `debug_heex_annotations` and `debug_attributes` for definition,207 caller, slot, line, and LiveView PID annotations.208209### Follow Phoenix-generated boundaries210211- Expect magic-link authentication by default and use generated212 `require_sudo_mode` for recently authenticated operations.213- Pass the generated application-owned scope through contexts, queries, foreign214 keys, PubSub topics, and authenticated LiveView sessions.215- Call app layout function components explicitly so each layout can accept its216 own assigns and slots.217- Treat Tailwind v4, daisyUI, themes, and the layout theme toggle as generator218 defaults, not requirements of `phx.gen.*` output.219220## Erlang/OTP quick reference221222- Send priority messages only through a priority alias and the `priority` send option; prioritize exit, link, and monitor signals through their APIs.223- Use strict comprehension generators (`<:-`, `<:=`) when non-matches must224 fail, and zip generators with `&&` for parallel iteration.225- Treat native records and comprehension assignment as experimental features.226- Prefer immutable `graph` when persistent graph versions are useful.227- Cap tar extraction with `{max_size, Size}`.228- Explicitly enable required SSH shell, exec, and SFTP services. SSL and SSH229 prefer hybrid ML-KEM-768/X25519 and fall back for older peers.230- Use `proc_lib` labels, independent `trace` sessions, unified `tprof`, and231 native coverage to diagnose runtime behavior.232233## Interoperability selection234235- Use Popcorn for an AtomVM WebAssembly subset in the browser or Hologram for236 Phoenix-based isomorphic components transpiled to JavaScript.237- Use Fine for signature-driven C++ NIFs or Zigler for inline Zig compiled at238 build time.239- Use Pythonx for in-process Python with `uv`-managed dependencies; account for240 GIL serialization unless native packages release it.241- Use the Swift Erlang Actor System when a Swift program must participate as a242 distributed node.