# Documentation Patterns Patterns extracted from the Elixir standard library source code. --- ## 1. @moduledoc with Structured Sections **Source:** `lib/elixir/lib/gen_server.ex` lines 6–100+, `lib/logger/lib/logger.ex` lines 6–200+ **What it does:** `@moduledoc` uses a clear hierarchical structure with `##` sections covering: overview, examples, configuration, and detailed behavioral explanations. GenServer covers "Example", "Client / Server APIs", "How to supervise", "Name registration", "Timeouts", "Debugging". Logger covers "Levels", "Message", "Metadata", "Configuration" (with subsections for Boot/Compile/Runtime). **Why:** Long moduledocs are effectively reference manuals. Structured sections let users jump to what they need. The pattern establishes a convention: start with "what is this" → "quick example" → "detailed topics." **Anti-pattern:** Writing a single unstructured wall of text, or documenting only the happy path without covering error handling, configuration, and advanced usage. **Code example:** ```elixir defmodule GenServer do @moduledoc """ A behaviour module for implementing the server of a client-server relation. A GenServer is a process like any other Elixir process... ## Example The GenServer behaviour abstracts the common client-server interaction... defmodule Stack do use GenServer # ... end ## How to supervise A GenServer is most commonly started under a supervision tree... ## Name registration ... ## Timeouts ... ## Debugging with the :sys module ... """ end ``` --- ## 2. @doc with Sections and Examples **Source:** `lib/elixir/lib/kernel.ex` lines 315–335 (abs/1), `lib/logger/lib/logger.ex` lines 536–540 **What it does:** `@doc` for individual functions includes: a brief one-liner, allowed-in-guards note (via `@doc guard: true`), explanation of edge cases, and `## Examples` with doctests. **Why:** Each function doc is self-contained. A developer reading docs shouldn't need to look elsewhere for basic usage. Guard-eligible functions are annotated structurally so tools can surface this. **Anti-pattern:** Docs that say "see module documentation" without providing any local context. Also: examples without `iex>` that can't be tested automatically. **Code example:** ```elixir @doc """ Returns all the available levels. """ @doc since: "1.16.0" @spec levels() :: [level(), ...] def levels(), do: @levels ``` --- ## 3. @doc since: Version Annotation **Source:** `lib/logger/lib/logger.ex` lines 539, 576, 813, 824, 831, `lib/elixir/lib/kernel.ex` line 5163+ **What it does:** Attaches `since: "X.Y.Z"` metadata to `@doc` indicating the Elixir version that introduced the function. **Why:** Users working with multiple Elixir versions need to know API availability. ExDoc renders this prominently. The pattern is universal in the standard library for any function added after 1.0. **Anti-pattern:** Adding new public functions without `@since` annotation, leaving version compatibility ambiguous. **Code example:** ```elixir @doc since: "1.15.0" @spec default_formatter(formatter_opts) :: {module, :logger.formatter_config()} def default_formatter(overrides \\ []) when is_list(overrides) do # ... end @doc since: "1.14.0" def binary_slice(binary, start, size) when is_binary(binary) and is_integer(start) and is_integer(size) and size >= 0 do # ... end ``` --- ## 4. @doc guard: true Metadata **Source:** `lib/elixir/lib/kernel.ex` lines 329, 408, 428, 452, etc. **What it does:** Functions/macros usable in guard clauses are annotated with `@doc guard: true` metadata, separate from the doc string itself. **Why:** This is machine-readable metadata. ExDoc and IEx can filter/display guard-eligible functions differently. It's orthogonal to the prose documentation. **Anti-pattern:** Mentioning "allowed in guards" only in the doc string text, which tools can't parse programmatically. **Code example:** ```elixir @doc """ Returns the absolute value of `number`. ... ## Examples iex> abs(-1) 1 iex> abs(1) 1 """ @doc guard: true @spec abs(number) :: number def abs(number) when is_number(number), do: ... ``` --- ## 5. @doc false — Hiding from Documentation **Source:** `lib/elixir/lib/inspect.ex` lines 410, 417; implicit via `@impl true` **What it does:** `@doc false` explicitly hides a function from documentation generators. Also, using `@impl true` automatically sets `@doc false` (unless `@doc` is explicitly provided). **Why:** Not all public functions are part of the user-facing API. Protocol implementations, internal helpers that must be public for technical reasons, and overridable defaults should be hidden from docs. **Anti-pattern:** Leaving documentation on functions that are public only for technical reasons (e.g., protocol dispatch functions), confusing users about what to call. **Code example:** ```elixir # Explicit hiding: @doc false def __protocol__(:module), do: __MODULE__ # Implicit via @impl: @impl true # automatically sets @doc false def init(counter) do {:ok, counter} end ``` --- ## 6. @moduledoc false — Hiding Modules **Source:** Common pattern in internal modules (not shown in top-level files but documented in `Module`) **What it does:** `@moduledoc false` makes an entire module invisible to documentation tools. **Why:** Internal implementation modules (e.g., `MyApp.Repo.Migrations.Internal`) exist for code organization but shouldn't appear in user-facing docs. **Anti-pattern:** Leaving `@moduledoc` undeclared on internal modules — ExDoc will show them with an empty documentation page, confusing users. **Code example:** ```elixir defmodule MyApp.Internal.Helper do @moduledoc false # This module exists but won't appear in docs def helper_function(x), do: x * 2 end ``` --- ## 7. Mermaid Diagrams in Documentation **Source:** `lib/elixir/lib/gen_server.ex` lines 14–20 **What it does:** Embeds Mermaid diagram syntax directly in `@moduledoc` to illustrate architectural patterns (client-server message flow). **Why:** Visual diagrams communicate architecture faster than prose. ExDoc renders Mermaid natively, so documentation can include live-rendered flowcharts. **Anti-pattern:** Describing complex interaction patterns only in prose when a diagram would be clearer. **Code example:** ```elixir @moduledoc """ A behaviour module for implementing the server of a client-server relation. ```mermaid graph BT C(Client #3) ~~~ B(Client #2) ~~~ A(Client #1) A & B & C -->|request| GenServer GenServer -.->|reply| A & B & C ``` ## Example ... """ ``` --- ## 8. Admonition Blocks in Documentation **Source:** `lib/elixir/lib/gen_server.ex` lines 88–95, `lib/elixir/lib/supervisor.ex` lines 34–38 **What it does:** Uses markdown admonition syntax (`> #### Title {: .info}`) to highlight important callouts — especially for `use ModuleName` behavior documentation. **Why:** Admonitions draw attention to critical information that might otherwise be buried in prose. The `.info`, `.warning`, `.neutral` classes provide visual differentiation. **Anti-pattern:** Burying critical behavioral information (like what `use` does) in regular paragraphs that users skim over. **Code example:** ```elixir @moduledoc """ ... > #### `use GenServer` {: .info} > > When you `use GenServer`, the `GenServer` module will > set `@behaviour GenServer` and define a `child_spec/1` > function, so your module can be used as a child > in a supervision tree. ... """ ``` --- ## 9. @doc deprecated: Soft Deprecation **Source:** `lib/elixir/lib/module.ex` lines 163–180 **What it does:** Uses `@doc deprecated: "Use X instead"` for soft deprecation (documentation-only warning) vs. `@deprecated "reason"` for hard deprecation (compiler warning). **Why:** Two levels of deprecation serve different needs. Soft deprecation signals "we recommend the alternative" without breaking builds. Hard deprecation actively warns at compile time. **Anti-pattern:** Using `@deprecated` for things that aren't truly deprecated yet (just discouraged), creating noise in build outputs. **Code example:** ```elixir # Soft deprecation — docs only, no compiler warning: @doc deprecated: "Use Kernel.length/1 instead" def size(keyword) do length(keyword) end # Hard deprecation — compiler warning emitted: @deprecated "Use Kernel.length/1 instead" def size(keyword) do length(keyword) end ``` --- ## 10. Callback Documentation Convention **Source:** `lib/elixir/lib/gen_server.ex` lines 584–646 (handle_call docs) **What it does:** Each `@callback` is preceded by a comprehensive `@doc` that explains: what triggers the callback, what the parameters mean, every possible return value and its effect, when the callback is optional, and cross-references to related callbacks. **Why:** Callback documentation is the primary teaching tool for behaviour implementers. Since users must write these functions, the docs must be complete enough to implement from. **Anti-pattern:** Documenting callbacks with only a one-liner, forcing users to read source code or external guides. **Code example:** ```elixir @doc """ Invoked to handle synchronous `call/3` messages. `call/3` will block until a reply is received (unless the call times out or nodes are disconnected). `request` is the request message sent by a `call/3`, `from` is a 2-tuple containing the caller's PID and a term that uniquely identifies the call, and `state` is the current state of the `GenServer`. Returning `{:reply, reply, new_state}` sends the response `reply` to the caller and continues the loop with new state `new_state`. Returning `{:noreply, new_state}` does not send a response to the caller and continues the loop with new state `new_state`. The response must be sent with `reply/2`. ... This callback is optional. If one is not implemented, the server will fail if a call is performed against it. """ @callback handle_call(request :: term, from, state :: term) :: {:reply, reply, new_state} | ... when reply: term, new_state: term, reason: term ``` --- ## 11. Documentation with Link References (c: and t: prefixes) **Source:** `lib/elixir/lib/gen_server.ex` throughout the @moduledoc **What it does:** Uses ExDoc link syntax like `c:init/1` (callback reference), `t:server/0` (type reference), and backtick function references like `` `start_link/2` `` to create navigable cross-references. **Why:** Documentation is a hypertext. Cross-linking lets users navigate between related concepts. The `c:` and `t:` prefixes disambiguate between functions, callbacks, and types with the same name. **Anti-pattern:** Mentioning related functions/types by name without linking them, requiring users to manually search. **Code example:** ```elixir @moduledoc """ ... `c:init/1` transforms our initial argument to the initial state for the GenServer. `c:handle_call/3` fires when the server receives a synchronous `pop` message... Each call to `GenServer.call/3` results in a message that must be handled by the `c:handle_call/3` callback in the GenServer. ... """ ```