Elixir Knowledge Patch
Use this index to load only the references relevant to the task. Apply the migration notes before adopting newer APIs, especially when upgrading an existing project or runtime.
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, and install the matching generator explicitly when upgrading:
mix archive.install hex phx_new 1.8.0 --force. - Treat Elixir 1.18 as the final Elixir release supporting OTP 25. Use OTP 26 or newer on Windows; WERL is unsupported.
Update source constructs
- Split chained
require(SomeModule).some_macro()code into arequire SomeModulestatement and a separate macro call.require/1no longer expands to module AST. - Pin an already-bound bitstring size:
<<value::size(^size)>>. - Match a struct before updating it:
def set_path(%URI{} = uri), do: %{uri | path: "/"}. - Remove recursive pattern-variable cycles and express equality with guards instead.
- Separate scripts in identifiers with underscores. Direct mixed-script identifiers are rejected, and bidirectional confusables warn.
- Replace implicit descending
Range.new(first, last)calls with an explicit negative step. - Expect raw carriage returns and U+2028/U+2029 source line breaks to be rejected in affected strings or comments.
Migrate deprecated APIs and options
- Use
File.stream!(path, lines_or_bytes, modes)in that argument order. - Replace
Logger.enable/1andLogger.disable/1withLogger.put_process_level/2andLogger.delete_process_level/1. - Replace Logger's
:backendsconfiguration by disabling:default_handleror starting custom backends from the application callback. - Move
xref: [exclude: ...]toelixirc_options: [no_warn_undefined: ...]. - Move
:default_task,:preferred_cli_env, and:preferred_cli_targetfromproject/0tocli/0as:default_task,:preferred_envs, and:preferred_targets. - Join
mix dotasks with+, rename--no-protocol-consolidationto--no-consolidate-protocols, and stop invoking inertmix compile.protocols. - Pass
--warnings-as-errorstomix compileormix test; do not set:warnings_as_errorsthrough compiler options. - Use
mix do --app APPinstead ofmix cmd --app APP. Remember thatmix cmdnow preserves quoting and skips shell expansion unless--shellprecedes the command. - Replace
List.zip/1,Module.eval_quoted/3,Tuple.append/2, andMix.Tasks.Compile.compilers/0withEnum.zip/1,Code.eval_quoted/3,Tuple.insert_at/3, andMix.Task.Compiler.compilers/0. - Use
<%!-- ... --%>or<% # ... %>for EEx comments, and implementEEx.handle_text/3instead of arity two. - Replace protocol
Any.__deriving__/3callbacks with a protocol-owned optional__deriving__/1macro.
Account for compiler and regex behavior
- Do not rely on project modules loading immediately during compilation. Use
Kernel.ParallelCompiler.pmap/2or callCode.ensure_compiled!/1before spawning compiler-time work. - Pass
return_diagnostics: truetoKernel.ParallelCompiler.compile,compile_to_path, andrequire. - Do not define a struct or exception inside
defprotocol. - Initialize regex struct fields at construction time rather than using compiled regexes as struct defaults on OTP 28.
- Recompile regexes per node and runtime version. OTP's PCRE2-backed
reparser rejects some formerly tolerated escapes, and its compiled representation is not portable. - Replace
Inspect.Algebra.next_break_fitswith optimistic or pessimistic groups. - Do not use
on_undefined_variable: :warn; undefined identifiers no longer fall back to function calls.
Update Ecto integrations
- Make adapters handle
distinct,group_by,order_by, andwindowasEcto.Query.ByExpr, notQueryExpr. - Initialize parameterized types with
Ecto.ParameterizedType.init/2; do not depend on their changed private tuple representation. - Remove the deleted
:array_joinjoin type. - Use
allow_stale: trueonly when intentionally accepting a stale struct or changeset write.
Upgrade LiveView wiring and tests
- Put
:phoenix_live_viewbefore standard Mix compilers, add LazyHTML for tests, and remove Floki only if nothing else uses it. - For colocated code, update esbuild, add
--alias:@=., and configureNODE_PATHfor dependency and build paths. - Rename pre-existing global hook names beginning with
.; leading-dot colocated hooks are now module-prefixed. - Replace Floki-only
fl-containsandfl-icontainsselectors with LiveViewTest text filters. - Fix duplicate DOM and LiveComponent IDs;
live/3andlive_isolated/3raise for duplicates by default. - Add
annotate_slot/4to customPhoenix.LiveView.TagEngineimplementations.
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. Use the Erlang json module directly when writing Erlang; its default object keys are binaries too.
Read type warnings structurally
- Expect inference across guards, anonymous functions, protocols, calls, return values, and every language construct.
- Read
dynamic(t)asdynamic() and t, not as an unconstrained escape hatch. - Write open maps with leading
..., optional fields withif_set(type), forbidden fields withnot_set(), and open tuples with a trailing.... - Remember that later clauses exclude inputs definitely accepted by earlier clauses.
- Treat another module in the same project as
dynamic()during local inference; whole-project checking still compares the 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.
Use newer core APIs
- Normalize calendar-style durations with
Kernel.to_timeout/1. - Use
File.read(path, [:raw])for a raw file read. ExpectFile.cp_r/3to skip special files, preserve directory permissions, and avoid symlink or nested-destination loops. - Import uppercase
/Eregular expressions withRegex.import/1; useRegex.to_embed/2to embed one regex in another. - Use
min/2andmax/2in guards. - Pass
{:via, module, term}names toPartitionSupervisor.count_children/1andstop/3. - Customize embedded
dbgevaluation with:dbg_callback; pipeline debugging now prints every intermediate stage.
Testing, compilation, and source tooling
Structure concurrent ExUnit suites
Parameterize an entire test module and keep modules sharing a resource out of concurrent execution:
use ExUnit.Case,
async: true,
group: :postgres,
parameterize: [%{partitions: 1}, %{partitions: 8}]
Read parameters 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.
Framework quick reference
Compose Ecto queries and schemas
- Use subqueries in
distinct,group_by,order_by, andwindow, literal maps indynamic/2, dynamic map-update values inselect, and anyEnumerableon the right of queryin. - Let root
order_bymacros expand to the complete ordering expression and preload subquery sources. - Use arity-two custom preload functions to receive both parent IDs and association metadata.
- Supply source-only or update-syntax queries to
Repo.insert_all/3; use broaderselect_mergesupport when fields are distinct. - Mark read-only fields with
writable: :never, defaultembeds_onevalues withdefaults_to_struct: true, and store durations with:duration.
Build LiveView interfaces
- Define colocated hooks with
Phoenix.LiveView.ColocatedHookand arbitrary colocated JavaScript withPhoenix.LiveView.ColocatedJS; merge generated hooks into theLiveSocketconfiguration. - Add
:keyto comprehensions when identity must survive insertion or reordering. Prefer streams for very large collections. - Render elsewhere in the DOM with
Phoenix.Component.portal/1while 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_annotationsanddebug_attributesfor definition, caller, slot, line, and LiveView PID annotations.
Follow Phoenix-generated boundaries
- Expect magic-link authentication by default and use generated
require_sudo_modefor 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
prioritysend option; prioritize exit, link, and monitor signals through their corresponding 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 OTP features.
- Prefer the immutable
graphmodule when persistent graph versions are useful. - Cap tar extraction with
{max_size, Size}. - Explicitly enable only required SSH shell, exec, and SFTP services. SSL and SSH prefer hybrid ML-KEM-768/X25519 and fall back for older peers.
- Use
proc_liblabels, independenttracesessions, unifiedtprof, 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.