# Elixir Language Source: Convention Reference Quick-reference for conventions extracted from the elixir-lang/elixir source code. Each entry: pattern name, location, example, when to use, when NOT to use, origin. --- ## Version-Gated TODOs **Location:** Throughout `lib/` ```elixir # TODO: Remove me on v2.0 # TODO: Deprecate me on Elixir v1.23 # TODO: Make an error on Elixir v2.0 ``` **When to use:** Any backward-compatible code that should be removed at a known future version. Deprecation paths, compatibility shims, feature flags. **When NOT to use:** Performance improvements ("make this faster someday"), refactoring desires, or anything without a clear version boundary. Those belong in issues, not TODOs. **Origin:** Consistent throughout history. The pattern predates the repo's earliest commits — José's convention from the start. --- ## Independent OTP Applications **Location:** `lib/` (6 top-level dirs) ``` lib/elixir/mix.exs # The language core lib/ex_unit/mix.exs # Testing framework lib/mix/mix.exs # Build tool lib/logger/mix.exs # Logging lib/iex/mix.exs # Interactive shell lib/eex/mix.exs # Templates ``` **When to use:** When components have distinct lifecycle, deployment, or dependency requirements. When you want components to be independently testable. **When NOT to use:** Small projects where the overhead of multiple applications exceeds the organizational benefit. If components always deploy together and never independently, a single app is simpler. **Origin:** Elixir 0.x — the language was always structured this way. --- ## Erlang for Performance-Critical Paths **Location:** `lib/elixir/src/` (33 .erl files) ```erlang %% elixir_tokenizer.erl — binary pattern matching is faster in Erlang %% elixir_erl_pass.erl — code generation benefits from proximity to BEAM ``` **When to use:** When Erlang's binary pattern matching or NIF interface provides measurable performance advantage. When code must exist before Elixir's compiler is available (bootstrap). **When NOT to use:** For new features that could be written in Elixir. The trajectory is always toward less Erlang. Don't write new Erlang unless profiling proves it's necessary. **Origin:** The entire language started as Erlang (2011). Bootstrap file (`elixir_bootstrap.erl`) formalized as distinct from "Elixir written in Erlang" in 2013 (commit `260be7c8e`). --- ## Protocol Consolidation **Location:** `lib/elixir/lib/protocol.ex` ```elixir # In mix.exs (default for prod): consolidate_protocols: true # Disable for tests: consolidate_protocols: Mix.env() != :test ``` **When to use:** Always in production (it's the default). Consolidated protocols dispatch in two function calls instead of dynamic lookup. **When NOT to use:** In tests where you define new protocol implementations at runtime. In dev if you're frequently recompiling protocol implementations. **Origin:** PR adding consolidation to escript.build (2014, issue #2699). Later made default for Mix projects. --- ## Code.Formatter via Inspect.Algebra **Location:** `lib/elixir/lib/code/formatter.ex` (2,605 lines) ```elixir # The formatter is a library function: Code.format_string!("def foo( x,y),do: x+y") # => "def foo(x, y), do: x + y" # Internally uses Wadler-Lindig algebra: import Inspect.Algebra, except: [format: 2, surround: 3, surround: 4] ``` **When to use:** When building code generation tools, macros that produce source, or custom formatting rules. The algebra is available to any Elixir code. **When NOT to use:** Don't reimplement formatting logic. Use `Code.format_string!/2` or the `mix format` task. The algebra is for building formatters, not for end-user formatting. **Origin:** Oct 7, 2017 (PR #6639, José Valim). Merged in 1 hour, zero review comments. --- ## Mix Task Convention **Location:** `lib/mix/lib/mix/tasks/` (55 files) ```elixir defmodule Mix.Tasks.Deps.Clean do @moduledoc "Removes the given dependencies' build artifacts." @shortdoc "Deletes generated files and artifacts for dependencies" use Mix.Task @recursive true @impl true def run(args) do # ... end end ``` **When to use:** Any command-line operation that should be available via `mix `. One file per task, module name determines task name. **When NOT to use:** Tasks that require interactive user input (use escript or IEx helpers instead). Tasks that need to run without Mix loaded. **Origin:** Part of Mix since its creation. The one-file convention is strict — all 55 stdlib tasks follow it. --- ## ExUnit.CaseTemplate **Location:** `lib/ex_unit/lib/ex_unit/case_template.ex` (162 lines) ```elixir defmodule MyApp.DataCase do use ExUnit.CaseTemplate setup tags do :ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo) unless tags[:async], do: Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()}) :ok end end # Usage: defmodule MyApp.SomeTest do use MyApp.DataCase, async: true end ``` **When to use:** When multiple test modules share setup logic, assertions, or helper functions. The inheritance model allows composition of test concerns. **When NOT to use:** For test helpers that don't need lifecycle callbacks. Simple `import` or `alias` is cleaner for utility functions that don't need `setup`/`setup_all`. **Origin:** Commit `1f491dfbe` — "Add support to case templates." The pattern Phoenix adopted for `ConnCase`/`DataCase` comes directly from ExUnit. --- ## Logger Wrapping OTP **Location:** `lib/logger/` ```elixir # Elixir's Logger dispatches to Erlang's :logger # Translation layer converts Erlang messages → Elixir format # Filters and handlers use OTP's infrastructure ``` **When to use:** When OTP provides the infrastructure you need. Wrap it — don't replace it. Provide Elixir-idiomatic API on top. **When NOT to use:** When OTP's solution has fundamental architectural limitations you can't work around with a wrapper (rare). **Origin:** PR #9333 (Sep-Nov 2019). Rewrote Logger from custom dispatch to wrapping Erlang's `:logger`. ~2 month implementation. --- ## BDFL Merge Pattern **Location:** Throughout git history ``` PR #6639 (Formatter): 0 comments, merged in 1 hour, 2,605 new lines PR #14021 (JSON): 38 review comments, 13 days, community input PR #13385 (Duration): 75+116 comments, 33 days, community redirected ``` **When to use:** Foundational infrastructure where one person holds the architectural vision. The BDFL can ship without review when the change is self-evidently correct (formatter) or redirect community work when the abstraction isn't right (Duration). **When NOT to use:** In team-maintained projects without a clear authority. In projects where consensus is required for API decisions. The BDFL model fails when the BDFL is wrong and no one can override. **Origin:** José Valim has been sole authority since Elixir's creation (2011). The core team exists but José has final say on language design. --- ## Type System Architecture (Set-Theoretic) **Location:** `lib/elixir/lib/module/types/` (13,034 lines, 7 files) ```elixir # Types are sets. Operations are set operations. # descr.ex (6,301 lines) defines the algebra: # - Union of types # - Intersection of types # - Difference (negation types) # - Subtype checking via set inclusion ``` **When to use:** Understanding how Elixir's gradual type system works internally. The set-theoretic approach means types compose naturally — `integer() | String.t()` is literally a set union. **When NOT to use:** This is internal compiler infrastructure. Don't depend on `Module.Types` internals — they change frequently (504 commits and counting). **Origin:** Aug 2019 (PR #9270 by Eric Meadows-Jönsson). Originally just function clause exhaustiveness checking, now growing into full gradual typing. 96% of subsequent work by José.