> ## Documentation Index
> Fetch the complete documentation index at: https://docs.codeant.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Elixir

> Learn about the Elixir anti-patterns detected by CodeAnt AI.

> [accordion] Use consistent exception names
>
> Exception modules should consistently use the conventional `Error` suffix.
>
> ```elixir Bad theme={"theme":"css-variables"}
> defmodule PaymentException, do: defexception([:message])
> defmodule RefundError, do: defexception([:message])
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> defmodule PaymentError, do: defexception([:message])
> defmodule RefundError, do: defexception([:message])
> ```

> [accordion] Use consistent line endings
>
> Mixing Unix (`LF`) and Windows (`CRLF`) line endings creates noisy diffs and inconsistent formatting.
>
> ```text Bad theme={"theme":"css-variables"}
> lib/user.ex     LF
> lib/account.ex  CRLF
> ```
>
> ```text Fix theme={"theme":"css-variables"}
> lib/user.ex     LF
> lib/account.ex  LF
> ```

> [accordion] Use consistent parameter pattern matching
>
> When naming a matched parameter, place the variable consistently before or after the pattern.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def parse({:ok, value} = result), do: {result, value}
> def save(result = {:ok, value}), do: {result, value}
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def parse({:ok, value} = result), do: {result, value}
> def save({:ok, value} = result), do: {result, value}
> ```

> [accordion] Use consistent spacing around operators
>
> Consistent whitespace around operators makes expressions easier to scan.
>
> ```elixir Bad theme={"theme":"css-variables"}
> total=price*quantity
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> total = price * quantity
> ```

> [accordion] Use consistent spacing inside parentheses
>
> Do not add unnecessary spaces immediately inside parentheses.
>
> ```elixir Bad theme={"theme":"css-variables"}
> calculate( price, quantity )
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> calculate(price, quantity)
> ```

> [accordion] Use consistent indentation
>
> Use spaces consistently instead of mixing tab- and space-indented lines.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def run do
>   prepare() # tab-indented in the source file
>   execute() # space-indented in the source file
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def run do
>   prepare()
>   execute()
> end
> ```

> [accordion] Alias nested modules
>
> Alias nested dependencies so a module's external dependencies are visible at a glance.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def search(query) do
>   MyApp.External.SearchClient.run(query)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> alias MyApp.External.SearchClient
>
> def search(query), do: SearchClient.run(query)
> ```

> [accordion] Resolve FIXME tags
>
> `FIXME` tags identify known broken or unsafe behavior that should be corrected.
>
> ```elixir Bad theme={"theme":"css-variables"}
> # FIXME: accept every role until authorization is implemented
> def authorized?(_user), do: true
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def authorized?(%User{role: role}), do: role in [:admin, :editor]
> ```

> [accordion] Resolve TODO tags
>
> Replace incomplete `TODO` placeholders with the intended implementation.
>
> ```elixir Bad theme={"theme":"css-variables"}
> # TODO: persist this user
> def create_user(attrs), do: {:ok, attrs}
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def create_user(attrs), do: Repo.insert(User.changeset(%User{}, attrs))
> ```

> [accordion] Order aliases consistently
>
> Keep aliases alphabetically ordered so dependencies are easy to find.
>
> ```elixir Bad theme={"theme":"css-variables"}
> alias MyApp.Users
> alias MyApp.Accounts
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> alias MyApp.Accounts
> alias MyApp.Users
> ```

> [accordion] Use conventional function names
>
> Elixir function names should use lowercase `snake_case` and may end in `?` or `!`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def LoadUser(user_id), do: Repo.get(User, user_id)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def load_user(user_id), do: Repo.get(User, user_id)
> ```

> [accordion] Make large numbers readable
>
> Add underscore separators to large numeric literals.
>
> ```elixir Bad theme={"theme":"css-variables"}
> upload_limit = 10000000
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> upload_limit = 10_000_000
> ```

> [accordion] Keep lines within the maximum length
>
> Split long expressions into named values or multiple lines.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Logger.info("created user #{user.id} for organization #{organization.id} with permissions #{inspect(permissions)}")
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> metadata = "organization #{organization.id}, permissions #{inspect(permissions)}"
> Logger.info("created user #{user.id} for #{metadata}")
> ```

> [accordion] Use conventional module attribute names
>
> Module attributes should use lowercase `snake_case`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> @RetryLimit 3
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @retry_limit 3
> ```

> [accordion] Document modules
>
> Public modules should explain their purpose with `@moduledoc`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> defmodule MyApp.Invoice do
>   def total(invoice), do: invoice.subtotal + invoice.tax
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> defmodule MyApp.Invoice do
>   @moduledoc "Calculates invoice totals."
>   def total(invoice), do: invoice.subtotal + invoice.tax
> end
> ```

> [accordion] Use conventional module names
>
> Module names should use `CamelCase` without underscores.
>
> ```elixir Bad theme={"theme":"css-variables"}
> defmodule MyApp.User_Profile do
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> defmodule MyApp.UserProfile do
> end
> ```

> [accordion] Avoid unnecessary parentheses in conditions
>
> Elixir conditions do not need surrounding parentheses.
>
> ```elixir Bad theme={"theme":"css-variables"}
> if (user.active?) do
>   send_welcome(user)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> if user.active? do
>   send_welcome(user)
> end
> ```

> [accordion] Avoid parentheses on zero-arity definitions
>
> Zero-arity function definitions conventionally omit parentheses.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def current_time(), do: DateTime.utc_now()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def current_time, do: DateTime.utc_now()
> ```

> [accordion] Avoid piping into anonymous functions
>
> Assign or invoke an anonymous function directly instead of hiding the call in a pipeline.
>
> ```elixir Bad theme={"theme":"css-variables"}
> value
> |> (fn item -> normalize(item) end).()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> normalizer = fn item -> normalize(item) end
> normalizer.(value)
> ```

> [accordion] Use conventional predicate function names
>
> Functions returning booleans should end in `?`; reserve `is_` names for guards.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def is_active(user), do: user.status == :active
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def active?(user), do: user.status == :active
> ```

> [accordion] Prefer implicit try
>
> Function-level `rescue`, `catch`, and `after` clauses are clearer without a redundant `try`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def load(path) do
>   try do
>     File.read!(path)
>   rescue
>     File.Error -> :error
>   end
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def load(path) do
>   File.read!(path)
> rescue
>   File.Error -> :error
> end
> ```

> [accordion] Remove redundant blank lines
>
> Use a single blank line to separate related sections.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def first, do: :ok
>
>
> def second, do: :ok
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def first, do: :ok
>
> def second, do: :ok
> ```

> [accordion] Avoid semicolons
>
> Put expressions on separate lines instead of separating them with semicolons.
>
> ```elixir Bad theme={"theme":"css-variables"}
> user = load_user(id); notify(user)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> user = load_user(id)
> notify(user)
> ```

> [accordion] Add spaces after commas
>
> Commas should be followed by a space.
>
> ```elixir Bad theme={"theme":"css-variables"}
> create_user(name,email,role)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> create_user(name, email, role)
> ```

> [accordion] Use string sigils when they improve readability
>
> Sigils remove distracting escape characters from strings containing many quotes.
>
> ```elixir Bad theme={"theme":"css-variables"}
> html = "<a href=\"/docs\" target=\"_blank\">Docs</a>"
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> html = ~S(<a href="/docs" target="_blank">Docs</a>)
> ```

> [accordion] End files with a trailing newline
>
> End every source file with exactly one newline for portable tooling and clean diffs.
>
> ```text Bad theme={"theme":"css-variables"}
> defmodule MyApp.User do
> end[EOF]
> ```
>
> ```text Fix theme={"theme":"css-variables"}
> defmodule MyApp.User do
> end
> [newline][EOF]
> ```

> [accordion] Remove trailing whitespace
>
> Remove invisible spaces and tabs at the end of source lines.
>
> ```text Bad theme={"theme":"css-variables"}
> name = "Ada"[four trailing spaces]
> ```
>
> ```text Fix theme={"theme":"css-variables"}
> name = "Ada"
> ```

> [accordion] Avoid unnecessary alias expansion
>
> Use the alias directly instead of repeating its namespace.
>
> ```elixir Bad theme={"theme":"css-variables"}
> alias MyApp.Accounts
> MyApp.Accounts.create_user(attrs)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> alias MyApp.Accounts
> Accounts.create_user(attrs)
> ```

> [accordion] Use conventional variable names
>
> Variables should use lowercase `snake_case`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> UserName = "Ada"
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> user_name = "Ada"
> ```

> [accordion] Use case for a single-clause with
>
> A `with` containing one match and an `else` branch is clearer as a `case`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> with {:ok, user} <- Accounts.fetch_user(id) do
>   user
> else
>   {:error, reason} -> raise "failed: #{inspect(reason)}"
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> case Accounts.fetch_user(id) do
>   {:ok, user} -> user
>   {:error, reason} -> raise "failed: #{inspect(reason)}"
> end
> ```

> [accordion] Prefer direct function calls over apply
>
> When the target and argument count are known, call the function directly.
>
> ```elixir Bad theme={"theme":"css-variables"}
> apply(String, :upcase, [name])
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> String.upcase(name)
> ```

> [accordion] Simplify short cond expressions
>
> A `cond` with one condition plus a fallback should be an `if`/`else`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> cond do
>   score >= 60 -> :pass
>   true -> :fail
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> if score >= 60 do
>   :pass
> else
>   :fail
> end
> ```

> [accordion] Reduce cyclomatic complexity
>
> Too many branches make functions difficult to understand and test. Extract named decisions.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def price(order) do
>   cond do
>     order.vip? and order.total > 100 -> order.total * 0.8
>     order.vip? -> order.total * 0.9
>     order.total > 100 -> order.total * 0.95
>     true -> order.total
>   end
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def price(order), do: order.total * discount(order)
> defp discount(%{vip?: true, total: total}) when total > 100, do: 0.8
> defp discount(%{vip?: true}), do: 0.9
> defp discount(%{total: total}) when total > 100, do: 0.95
> defp discount(_order), do: 1.0
> ```

> [accordion] Use Enum.count with a predicate
>
> Count matching elements in one pass with `Enum.count/2`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> users
> |> Enum.filter(& &1.active?)
> |> Enum.count()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Enum.count(users, & &1.active?)
> ```

> [accordion] Combine consecutive filters
>
> Combining predicates avoids traversing an intermediate collection.
>
> ```elixir Bad theme={"theme":"css-variables"}
> users
> |> Enum.filter(& &1.active?)
> |> Enum.filter(&(&1.age >= 18))
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Enum.filter(users, fn user ->
>   user.active? and user.age >= 18
> end)
> ```

> [accordion] Limit function arity
>
> Too many parameters make calls error-prone. Group related values into a struct or map.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def create_user(name, email, role, team, locale, timezone) do
>   # ...
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def create_user(%UserParams{} = params) do
>   # ...
> end
> ```

> [accordion] Keep quote blocks concise
>
> Large quoted AST blocks should delegate to smaller macros or helper functions.
>
> ```elixir Bad theme={"theme":"css-variables"}
> quote do
>   def start, do: :ok
>   def stop, do: :ok
>   def restart, do: :ok
>   def status, do: :running
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> quote do
>   unquote(lifecycle_functions())
>   unquote(status_function())
> end
> ```

> [accordion] Use Enum.map\_join
>
> Map and join in one traversal with `Enum.map_join/3`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> users
> |> Enum.map(& &1.name)
> |> Enum.join(", ")
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Enum.map_join(users, ", ", & &1.name)
> ```

> [accordion] Avoid matches in conditions
>
> Use `case` when control flow depends on the shape of a value.
>
> ```elixir Bad theme={"theme":"css-variables"}
> if {:ok, contents} = File.read(path) do
>   process(contents)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> case File.read(path) do
>   {:ok, contents} -> process(contents)
>   {:error, reason} -> handle_error(reason)
> end
> ```

> [accordion] Avoid negated conditions in unless
>
> A negated `unless` condition is a double negative.
>
> ```elixir Bad theme={"theme":"css-variables"}
> unless not user.active? do
>   notify(user)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> if user.active? do
>   notify(user)
> end
> ```

> [accordion] Invert negated conditions with else
>
> Put the positive condition first and swap the branches.
>
> ```elixir Bad theme={"theme":"css-variables"}
> if not authorized?(user) do
>   {:error, :forbidden}
> else
>   perform_action(user)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> if authorized?(user) do
>   perform_action(user)
> else
>   {:error, :forbidden}
> end
> ```

> [accordion] Reduce control-flow nesting
>
> Extract nested decisions into functions or use pattern matching and early returns.
>
> ```elixir Bad theme={"theme":"css-variables"}
> if user do
>   if user.active? do
>     if user.admin?, do: grant_access(user)
>   end
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def grant_if_allowed(%User{active?: true, admin?: true} = user),
>   do: grant_access(user)
>
> def grant_if_allowed(_user), do: {:error, :forbidden}
> ```

> [accordion] Remove redundant with clause results
>
> Do not match the final result only to return the identical value.
>
> ```elixir Bad theme={"theme":"css-variables"}
> with {:ok, user} <- Accounts.fetch_user(id),
>      {:ok, result} <- authorize(user) do
>   {:ok, result}
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> with {:ok, user} <- Accounts.fetch_user(id) do
>   authorize(user)
> end
> ```

> [accordion] Combine consecutive rejects
>
> Combine predicates to avoid traversing an intermediate collection.
>
> ```elixir Bad theme={"theme":"css-variables"}
> users
> |> Enum.reject(& &1.disabled?)
> |> Enum.reject(& &1.deleted?)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Enum.reject(users, fn user ->
>   user.disabled? or user.deleted?
> end)
> ```

> [accordion] Avoid unless with else
>
> An `unless` with an `else` branch is easier to understand as an `if`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> unless user.active? do
>   deactivate_session(user)
> else
>   refresh_session(user)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> if user.active? do
>   refresh_session(user)
> else
>   deactivate_session(user)
> end
> ```

> [accordion] Keep with clauses focused on matching
>
> Move ordinary assignments before `with` and side-effect calls into its body.
>
> ```elixir Bad theme={"theme":"css-variables"}
> with ref = make_ref(),
>      {:ok, user} <- Accounts.create_user(ref),
>      Logger.info("created #{user.id}") do
>   user
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> ref = make_ref()
>
> with {:ok, user} <- Accounts.create_user(ref) do
>   Logger.info("created #{user.id}")
>   user
> end
> ```

> [accordion] Do not store runtime configuration in module attributes
>
> Module attributes are evaluated at compile time and can freeze environment-specific configuration.
>
> ```elixir Bad theme={"theme":"css-variables"}
> @pool_size Application.get_env(:my_app, :pool_size)
> def pool_size, do: @pool_size
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def pool_size do
>   Application.fetch_env!(:my_app, :pool_size)
> end
> ```

> [accordion] Avoid boolean operations on the same value
>
> Repeating the same boolean operand is redundant and may hide a typo.
>
> ```elixir Bad theme={"theme":"css-variables"}
> ready? and ready?
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> ready?
> ```

> [accordion] Remove dbg calls
>
> `dbg/2` is intended for local debugging and should not remain in committed application code.
>
> ```elixir Bad theme={"theme":"css-variables"}
> user = Accounts.fetch_user!(id) |> dbg()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> user = Accounts.fetch_user!(id)
> ```

> [accordion] Avoid expensive empty-enumerable checks
>
> Use `Enum.empty?/1` instead of traversing an enumerable to count every element.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Enum.count(users) == 0
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Enum.empty?(users)
> ```

> [accordion] Remove IEx.pry calls
>
> `IEx.pry` pauses a running process and must not remain in application code.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def handle_request(conn) do
>   IEx.pry()
>   process(conn)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def handle_request(conn) do
>   process(conn)
> end
> ```

> [accordion] Remove IO.inspect calls
>
> Replace temporary console inspection with intentional structured logging when diagnostics are needed.
>
> ```elixir Bad theme={"theme":"css-variables"}
> user
> |> IO.inspect(label: "loaded user")
> |> authorize()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> Logger.debug("loaded user", user_id: user.id)
> authorize(user)
> ```

> [accordion] Configure Logger metadata keys
>
> Metadata passed to Logger should be included in the formatter configuration so it is not silently omitted.
>
> ```elixir Bad theme={"theme":"css-variables"}
> # config: metadata: [:request_id]
> Logger.error("payment failed", error_code: :card_declined)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> # config: metadata: [:request_id, :error_code]
> Logger.error("payment failed", error_code: :card_declined)
> ```

> [accordion] Avoid operations on the same value
>
> Using the same expression on both sides often produces a trivial result or signals a copy-paste error.
>
> ```elixir Bad theme={"theme":"css-variables"}
> remaining = total - total
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> remaining = total - paid
> ```

> [accordion] Avoid operations with constant results
>
> Replace expressions whose result cannot change with the value they represent.
>
> ```elixir Bad theme={"theme":"css-variables"}
> discounted_total = total * 0
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> discounted_total = 0
> ```

> [accordion] Preserve stack traces when raising inside rescue
>
> Use `reraise/2` when translating an exception so the original stack trace is retained.
>
> ```elixir Bad theme={"theme":"css-variables"}
> rescue
>   error -> raise MyApp.LoadError, message: Exception.message(error)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> rescue
>   error ->
>     wrapped = MyApp.LoadError.exception(message: Exception.message(error))
>     reraise wrapped, __STACKTRACE__
> ```

> [accordion] Reference struct types correctly in specs
>
> Referencing `Module.t()` avoids an unnecessary compile-time dependency on a struct literal.
>
> ```elixir Bad theme={"theme":"css-variables"}
> @spec send_email(%MyApp.User{}) :: :ok
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @spec send_email(MyApp.User.t()) :: :ok
> ```

> [accordion] Avoid unsafe command execution
>
> Do not send a constructed command string through a shell. Pass a fixed executable and separate arguments.
>
> ```elixir Bad theme={"theme":"css-variables"}
> :os.cmd(~c"convert #{input_path} output.png")
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> System.cmd("convert", [input_path, "output.png"])
> ```

> [accordion] Use Enum operation results
>
> `Enum` functions return new values; they do not mutate the input collection.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Enum.sort(users)
> render(users)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> sorted_users = Enum.sort(users)
> render(sorted_users)
> ```

> [accordion] Use File operation results
>
> Handle the success or error returned by `File` operations.
>
> ```elixir Bad theme={"theme":"css-variables"}
> File.write(path, contents)
> :ok
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> case File.write(path, contents) do
>   :ok -> :ok
>   {:error, reason} -> {:error, reason}
> end
> ```

> [accordion] Use Keyword operation results
>
> `Keyword` functions return updated lists and do not mutate the original.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Keyword.put(options, :timeout, 5_000)
> request(options)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> options = Keyword.put(options, :timeout, 5_000)
> request(options)
> ```

> [accordion] Use List operation results
>
> `List` functions return updated lists and do not mutate the original.
>
> ```elixir Bad theme={"theme":"css-variables"}
> List.delete(users, deleted_user)
> notify(users)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> users = List.delete(users, deleted_user)
> notify(users)
> ```

> [accordion] Use Path operation results
>
> Use the path returned by `Path` functions.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Path.expand(path)
> File.read(path)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> expanded_path = Path.expand(path)
> File.read(expanded_path)
> ```

> [accordion] Use Regex operation results
>
> Use the value returned by `Regex` operations.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Regex.replace(~r/\s+/, value, " ")
> save(value)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> normalized = Regex.replace(~r/\s+/, value, " ")
> save(normalized)
> ```

> [accordion] Use String operation results
>
> Strings are immutable, so transformation results must be used.
>
> ```elixir Bad theme={"theme":"css-variables"}
> String.trim(name)
> create_user(name)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> name = String.trim(name)
> create_user(name)
> ```

> [accordion] Use Tuple operation results
>
> Tuple operations return a new tuple and do not mutate the original.
>
> ```elixir Bad theme={"theme":"css-variables"}
> Tuple.append(point, z)
> draw(point)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> point = Tuple.append(point, z)
> draw(point)
> ```

> [accordion] Use the correct test file extension
>
> ExUnit test files should use `.exs` so they are executed as scripts by the test runner.
>
> ```text Bad theme={"theme":"css-variables"}
> test/my_app/user_test.ex
> ```
>
> ```text Fix theme={"theme":"css-variables"}
> test/my_app/user_test.exs
> ```

> [accordion] Do not evaluate untrusted Elixir code
>
> Values derived from a request must not reach `Code.eval_*`, which executes code with the application's privileges.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def evaluate(conn) do
>   Code.eval_string(conn.params["expression"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def evaluate(conn) do
>   expression = conn.params["expression"]
>   SafeExpressionParser.evaluate(expression)
> end
> ```

> [accordion] Do not dynamically apply untrusted functions
>
> Untrusted values must not choose the module or function passed to `apply/3`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def dispatch(params) do
>   apply(String.to_existing_atom(params["module"]), params["function"], [])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def dispatch(%{"action" => "refresh"}), do: Sessions.refresh()
> def dispatch(%{"action" => "logout"}), do: Sessions.logout()
> def dispatch(_params), do: {:error, :unsupported_action}
> ```

> [accordion] Do not evaluate untrusted EEx templates
>
> EEx templates can execute Elixir code, so only application-owned template source should be evaluated.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def preview(conn) do
>   EEx.eval_string(conn.params["template"], assigns: conn.assigns)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def preview(conn) do
>   Phoenix.Template.render(MyAppWeb, "preview", "html", conn.assigns)
> end
> ```

> [accordion] Do not load untrusted native libraries
>
> An attacker-controlled path passed to `:erlang.load_nif` can execute arbitrary native code.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def load_native(conn) do
>   :erlang.load_nif(conn.params["library"], 0)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @library Path.join(:code.priv_dir(:my_app), "native/my_app_nif")
> def load_native, do: :erlang.load_nif(@library, 0)
> ```

> [accordion] Avoid command injection through :os.cmd
>
> `:os.cmd` invokes a command processor and must not receive constructed or untrusted commands.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def lookup(conn) do
>   :os.cmd(~c"nslookup #{conn.params["host"]}")
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def lookup(conn) do
>   host = validate_hostname!(conn.params["host"])
>   System.cmd("nslookup", [host])
> end
> ```

> [accordion] Avoid command injection through Port.open
>
> Do not let untrusted input select a command or shell string passed to `Port.open`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def run(conn) do
>   Port.open({:spawn, conn.params["command"]}, [:binary])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def run(argument) do
>   executable = System.find_executable("convert")
>   Port.open({:spawn_executable, executable}, [{:args, [argument]}, :binary])
> end
> ```

> [accordion] Validate System.cmd commands and arguments
>
> Use a fixed executable and validate every argument before passing it to `System.cmd`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def convert(conn) do
>   System.cmd(conn.params["program"], conn.params["arguments"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def convert(conn) do
>   input = validate_image_path!(conn.params["input"])
>   System.cmd("convert", [input, "output.png"])
> end
> ```

> [accordion] Avoid command injection through System.shell
>
> `System.shell` invokes a system shell and must not receive untrusted input.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def archive(conn) do
>   System.shell("tar -czf archive.tgz #{conn.params["path"]}")
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def archive(conn) do
>   path = validate_archive_path!(conn.params["path"])
>   System.cmd("tar", ["-czf", "archive.tgz", path])
> end
> ```

> [accordion] Validate Phoenix WebSocket origins
>
> Disabling socket origin checks allows malicious sites to open authenticated cross-origin WebSocket connections.
>
> ```elixir Bad theme={"theme":"css-variables"}
> socket "/socket", MyAppWeb.UserSocket,
>   websocket: [check_origin: false]
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> socket "/socket", MyAppWeb.UserSocket,
>   websocket: [check_origin: ["https://app.example.com"]]
> ```

> [accordion] Avoid wildcard CORS origins
>
> A wildcard CORS policy can allow arbitrary websites to read authenticated responses.
>
> ```elixir Bad theme={"theme":"css-variables"}
> plug CORSPlug, origin: "*"
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> plug CORSPlug, origin: ["https://app.example.com"]
> ```

> [accordion] Do not reuse state-changing actions for GET routes
>
> A controller action that changes state must not also be reachable by GET, which is not protected as a state-changing request.
>
> ```elixir Bad theme={"theme":"css-variables"}
> get "/account/delete", AccountController, :delete
> delete "/account", AccountController, :delete
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> get "/account", AccountController, :show
> delete "/account", AccountController, :delete
> ```

> [accordion] Disable Phoenix debug errors outside development
>
> Detailed error pages can disclose stack traces, source code, and request data.
>
> ```elixir Bad theme={"theme":"css-variables"}
> config :my_app, MyAppWeb.Endpoint,
>   debug_errors: true
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> config :my_app, MyAppWeb.Endpoint,
>   debug_errors: false
> ```

> [accordion] Enable Phoenix force\_ssl in production
>
> Production endpoints should redirect HTTP to HTTPS and enable transport protections.
>
> ```elixir Bad theme={"theme":"css-variables"}
> config :my_app, MyAppWeb.Endpoint,
>   force_ssl: false
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> config :my_app, MyAppWeb.Endpoint,
>   force_ssl: [rewrite_on: [:x_forwarded_proto]]
> ```

> [accordion] Secure session cookies
>
> Session cookies should use `secure`, `http_only`, and an appropriate SameSite policy.
>
> ```elixir Bad theme={"theme":"css-variables"}
> plug Plug.Session,
>   store: :cookie,
>   key: "_my_app_key",
>   secure: false,
>   http_only: false
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> plug Plug.Session,
>   store: :cookie,
>   key: "_my_app_key",
>   secure: true,
>   http_only: true,
>   same_site: "Lax"
> ```

> [accordion] Configure a Content Security Policy
>
> Secure browser headers should include a restrictive Content Security Policy to reduce the impact of XSS.
>
> ```elixir Bad theme={"theme":"css-variables"}
> plug :put_secure_browser_headers, %{}
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> plug :put_secure_browser_headers, %{
>   "content-security-policy" => "default-src 'self'; object-src 'none'"
> }
> ```

> [accordion] Enable CSRF protection in browser pipelines
>
> Every browser pipeline that fetches a cookie-backed session and accepts state changes should use `:protect_from_forgery`.
>
> ```elixir Bad theme={"theme":"css-variables"}
> pipeline :browser do
>   plug :accepts, ["html"]
>   plug :fetch_session
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> pipeline :browser do
>   plug :accepts, ["html"]
>   plug :fetch_session
>   plug :protect_from_forgery
> end
> ```

> [accordion] Enable secure browser headers
>
> HTML pipelines should install Phoenix's standard security headers.
>
> ```elixir Bad theme={"theme":"css-variables"}
> pipeline :browser do
>   plug :accepts, ["html"]
>   plug :fetch_session
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> pipeline :browser do
>   plug :accepts, ["html"]
>   plug :fetch_session
>   plug :put_secure_browser_headers
> end
> ```

> [accordion] Use cryptographically secure randomness
>
> Pseudo-random generators such as `:rand` must not create tokens, credentials, or other security-sensitive values.
>
> ```elixir Bad theme={"theme":"css-variables"}
> reset_token = :rand.uniform(1_000_000) |> Integer.to_string()
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> reset_token =
>   :crypto.strong_rand_bytes(32)
>   |> Base.url_encode64(padding: false)
> ```

> [accordion] Avoid weak cryptographic hashes
>
> MD5 and SHA-1 are collision-prone. Use SHA-256 or stronger for integrity and a password-hashing function for passwords.
>
> ```elixir Bad theme={"theme":"css-variables"}
> digest = :crypto.hash(:sha, payload)
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> digest = :crypto.hash(:sha256, payload)
> ```

> [accordion] Avoid atom-table exhaustion
>
> Atoms are not garbage collected, so untrusted strings must not be converted into new atoms.
>
> ```elixir Bad theme={"theme":"css-variables"}
> role = String.to_atom(conn.params["role"])
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def parse_role("admin"), do: {:ok, :admin}
> def parse_role("member"), do: {:ok, :member}
> def parse_role(_role), do: {:error, :invalid_role}
> ```

> [accordion] Do not compile untrusted regular expressions
>
> Attacker-controlled patterns can trigger excessive backtracking and consume scheduler time.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def search(conn, value) do
>   Regex.run(Regex.compile!(conn.params["pattern"]), value)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @allowed_patterns %{"email" => ~r/^[^\s]+@[^\s]+$/}
> def search(conn, value) do
>   Regex.run(Map.fetch!(@allowed_patterns, conn.params["pattern"]), value)
> end
> ```

> [accordion] Do not deserialize untrusted Erlang terms
>
> `:erlang.binary_to_term` can allocate attacker-controlled terms and deserialize executable functions.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def decode(conn) do
>   :erlang.binary_to_term(conn.body_params["data"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def decode(conn) do
>   Jason.decode(conn.body_params["data"])
> end
> ```

> [accordion] Prevent open redirects
>
> Do not let a request parameter select an arbitrary external redirect destination.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def continue(conn, %{"next" => next}) do
>   redirect(conn, external: next)
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def continue(conn, %{"next" => "dashboard"}) do
>   redirect(conn, to: ~p"/dashboard")
> end
>
> def continue(conn, _params), do: redirect(conn, to: ~p"/")
> ```

> [accordion] Prevent filesystem path traversal
>
> Do not pass request-controlled paths directly to filesystem APIs.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def read(conn) do
>   File.read(conn.params["path"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @documents %{"guide" => "guide.pdf", "terms" => "terms.pdf"}
> def read(conn) do
>   filename = Map.fetch!(@documents, conn.params["document"])
>   File.read(Path.join(:code.priv_dir(:my_app), "documents/#{filename}"))
> end
> ```

> [accordion] Prevent path traversal in send\_download
>
> Resolve a trusted identifier to a server-owned file instead of accepting a download path from the request.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def download(conn, %{"path" => path}) do
>   send_download(conn, {:file, path})
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def download(conn, %{"report" => report_id}) do
>   report = Reports.fetch_authorized!(conn.assigns.current_user, report_id)
>   send_download(conn, {:file, report.stored_path})
> end
> ```

> [accordion] Prevent path traversal in send\_file
>
> Only send files resolved beneath an application-owned directory.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def asset(conn) do
>   Plug.Conn.send_file(conn, 200, conn.params["path"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @assets %{"logo" => "logo.svg"}
> def asset(conn) do
>   filename = Map.fetch!(@assets, conn.params["asset"])
>   path = Path.join(:code.priv_dir(:my_app), "static/#{filename}")
>   Plug.Conn.send_file(conn, 200, path)
> end
> ```

> [accordion] Use parameterized Ecto SQL queries
>
> Never interpolate request values into raw SQL. Keep SQL text fixed and pass values as parameters.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def find_user(conn) do
>   Ecto.Adapters.SQL.query!(Repo, "SELECT * FROM users WHERE id = #{conn.params["id"]}")
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def find_user(conn) do
>   Ecto.Adapters.SQL.query!(
>     Repo,
>     "SELECT * FROM users WHERE id = $1",
>     [conn.params["id"]]
>   )
> end
> ```

> [accordion] Prevent server-side request forgery
>
> Do not let untrusted input control an outbound request URL. Resolve destinations from an allowlist.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def fetch(conn) do
>   Req.get!(conn.params["url"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @services %{"billing" => "https://billing.example.com/health"}
> def fetch(conn) do
>   Req.get!(Map.fetch!(@services, conn.params["service"]))
> end
> ```

> [accordion] Verify TLS certificates
>
> Disabling certificate verification allows a network attacker to impersonate the remote service.
>
> ```elixir Bad theme={"theme":"css-variables"}
> :httpc.request(:get, {url, []}, [ssl: [verify: :verify_none]], [])
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> :httpc.request(
>   :get,
>   {url, []},
>   [ssl: [verify: :verify_peer, cacerts: :public_key.cacerts_get()]],
>   []
> )
> ```

> [accordion] Do not let users select response content types
>
> An attacker who selects `text/html` may turn otherwise inert response data into executable script.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def show(conn) do
>   conn
>   |> put_resp_content_type(conn.params["content_type"])
>   |> send_resp(200, conn.params["body"])
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def show(conn) do
>   conn
>   |> put_resp_content_type("application/json")
>   |> send_resp(200, Jason.encode!(%{message: conn.params["body"]}))
> end
> ```

> [accordion] Escape dynamic Phoenix HTML responses
>
> Do not concatenate untrusted data into an HTML response. Render it through Phoenix's escaping.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def welcome(conn, %{"name" => name}) do
>   html(conn, "<h1>Welcome #{name}</h1>")
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def welcome(conn, %{"name" => name}) do
>   render(conn, :welcome, name: name)
> end
> ```

> [accordion] Do not pass untrusted HTML to raw
>
> `raw` bypasses Phoenix HTML escaping and can turn request data into executable markup.
>
> ```elixir Bad theme={"theme":"css-variables"}
> raw(@conn.params["message"])
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> @conn.params["message"]
> ```

> [accordion] Do not send untrusted HTML with send\_resp
>
> Use a template or an encoder with an explicit non-HTML content type instead of constructing response bodies from request data.
>
> ```elixir Bad theme={"theme":"css-variables"}
> def echo(conn) do
>   send_resp(conn, 200, "<p>#{conn.params["message"]}</p>")
> end
> ```
>
> ```elixir Fix theme={"theme":"css-variables"}
> def echo(conn) do
>   body = Jason.encode!(%{message: conn.params["message"]})
>
>   conn
>   |> put_resp_content_type("application/json")
>   |> send_resp(200, body)
> end
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.