Files
elixir-patterns/patterns/documentation.md
T

1002 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Documentation Patterns
Patterns extracted from the Elixir standard library source code.
---
## 1. @moduledoc with Structured Sections
**Source:** `lib/elixir/lib/gen_server.ex` lines 6100+, `lib/logger/lib/logger.ex` lines 6200+
**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
```
### When to Use
**Triggers:**
- Your module is the primary entry point for a concept (GenServer, Logger, Supervisor)
- The module has more than 3-4 public functions with non-obvious relationships
- Users need to understand configuration, lifecycle, or architecture before calling individual functions
**Example — before:**
```elixir
defmodule MyApp.Cache do
@moduledoc "A cache module."
def get(key), do: ...
def put(key, value, opts), do: ...
def invalidate(key), do: ...
end
```
**Example — after:**
```elixir
defmodule MyApp.Cache do
@moduledoc """
A write-through cache backed by ETS with configurable TTL.
## Usage
MyApp.Cache.put("user:1", user, ttl: :timer.minutes(5))
MyApp.Cache.get("user:1") #=> {:ok, %User{}}
## Configuration
Configure in your application config:
config :my_app, MyApp.Cache,
max_size: 10_000,
default_ttl: :timer.hours(1)
## Eviction
When `max_size` is reached, entries are evicted LRU-first...
"""
end
```
### When NOT to Use
**Don't use this when:**
- The module has 1-2 functions and the purpose is obvious from the module name
- It's an internal/private module (`@moduledoc false` is better)
- The module is a thin wrapper where function-level docs suffice
**Over-application example:**
```elixir
defmodule MyApp.StringUtils do
@moduledoc """
# String Utilities
## Overview
This module provides string utility functions for the application.
## Architecture
Functions in this module operate on binaries and return binaries...
## Configuration
No configuration required.
## Examples
See individual function documentation.
"""
def capitalize_words(str), do: ...
end
```
**Better alternative:**
```elixir
defmodule MyApp.StringUtils do
@moduledoc "String transformation helpers for display formatting."
@doc "Capitalizes the first letter of each word."
def capitalize_words(str), do: ...
end
```
**Why:** A utility module with a few pure functions doesn't need architecture docs, configuration sections, or a table of contents. Match the documentation depth to the module's complexity.
---
## 2. @doc with Sections and Examples
**Source:** `lib/elixir/lib/kernel.ex` lines 315335 (abs/1), `lib/logger/lib/logger.ex` lines 536540
**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
```
### When to Use
**Triggers:**
- The function has non-obvious behavior, edge cases, or preconditions
- Users need to understand what happens with boundary inputs (nil, empty list, negative numbers)
- The function is part of a public API that others will call without reading the source
**Example — before:**
```elixir
def chunk(list, size), do: ...
```
**Example — after:**
```elixir
@doc """
Splits `list` into chunks of the given `size`.
The last chunk may have fewer than `size` elements if the list
length is not evenly divisible.
## Examples
iex> chunk([1, 2, 3, 4, 5], 2)
[[1, 2], [3, 4], [5]]
iex> chunk([], 3)
[]
"""
def chunk(list, size), do: ...
```
### When NOT to Use
**Don't use this when:**
- The function is private (use `# comments` for private function notes)
- The function name + typespec are completely self-explanatory (e.g., `@spec pid() :: pid()`)
- You're implementing a behaviour callback and want it hidden (`@impl true` sets `@doc false` automatically)
**Over-application example:**
```elixir
@doc """
Returns the name.
## Examples
iex> get_name(%User{name: "Alice"})
"Alice"
"""
@spec get_name(User.t()) :: String.t()
def get_name(%User{name: name}), do: name
```
**Better alternative:**
```elixir
@spec get_name(User.t()) :: String.t()
def get_name(%User{name: name}), do: name
```
**Why:** When the function name, spec, and implementation are all trivially obvious, a `@doc` that restates them adds maintenance burden without helping anyone. Save detailed docs for functions where the behavior isn't immediately obvious.
---
## 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
```
### When to Use
**Triggers:**
- You're adding a new public function to an existing library (anything post-1.0)
- Your library publishes versioned documentation (via ExDoc/HexDocs)
- Users may be on older versions and need to know when a function became available
**Example — before:**
```elixir
@doc "Compacts the list by removing nil values."
def compact(list), do: Enum.reject(list, &is_nil/1)
```
**Example — after:**
```elixir
@doc since: "1.4.0"
@doc "Compacts the list by removing nil values."
def compact(list), do: Enum.reject(list, &is_nil/1)
```
### When NOT to Use
**Don't use this when:**
- You're writing application code (not a published library)
- The function has existed since the library's first release
- The library doesn't publish versioned docs or follow semver
**Over-application example:**
```elixir
# In a Phoenix controller — no one checks "which version of my app added this"
defmodule MyAppWeb.UserController do
@doc since: "0.1.0"
def index(conn, _params), do: ...
@doc since: "0.2.0"
def show(conn, %{"id" => id}), do: ...
end
```
**Better alternative:**
```elixir
defmodule MyAppWeb.UserController do
def index(conn, _params), do: ...
def show(conn, %{"id" => id}), do: ...
end
```
**Why:** `since:` annotations help library consumers check compatibility. Application code is deployed atomically — there's no concept of "which version of the app introduced this endpoint." Use git blame instead.
---
## 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: ...
```
### When to Use
**Triggers:**
- The function/macro is valid in guard clauses
- You want tools (ExDoc, IEx) to programmatically identify guard-eligible functions
- The function is a Kernel macro that users might try to use in guards
**Example — before:**
```elixir
@doc """
Returns true if `term` is a non-empty binary.
Allowed in guard tests.
"""
defmacro is_non_empty_binary(term) do
...
end
```
**Example — after:**
```elixir
@doc """
Returns true if `term` is a non-empty binary.
"""
@doc guard: true
defmacro is_non_empty_binary(term) do
...
end
```
### When NOT to Use
**Don't use this when:**
- The function is NOT valid in guard clauses (adding it would be misleading)
- You're writing application code where no one filters functions by guard eligibility
- The function calls other functions or has side effects (it can't be a guard)
**Over-application example:**
```elixir
@doc guard: true
def valid_email?(email) do
String.contains?(email, "@") # NOT guard-safe — calls String.contains?
end
```
**Better alternative:**
```elixir
@doc "Checks if the string looks like an email address."
def valid_email?(email) do
String.contains?(email, "@")
end
```
**Why:** `@doc guard: true` is a semantic contract that the function works in guard position. Adding it to non-guard functions breaks tooling expectations and confuses users who try to use it in `when` clauses.
---
## 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
```
### When to Use
**Triggers:**
- A function must be public for technical reasons (protocol dispatch, macro expansion) but isn't part of the user API
- You're implementing callbacks with `@impl true` and don't want generated docs
- The function is an internal hook that users should never call directly
**Example — before:**
```elixir
# Users see this in docs and try to call it
def __struct__(fields), do: ...
```
**Example — after:**
```elixir
@doc false
def __struct__(fields), do: ...
```
### When NOT to Use
**Don't use this when:**
- The function IS part of the public API (even if you think it's "obvious")
- You want to discourage use but still document it (use `@doc deprecated:` instead)
- You're hiding functions because you're too lazy to document them
**Over-application example:**
```elixir
defmodule MyApp.Repo do
@doc false
def get(schema, id), do: ...
@doc false
def all(query), do: ...
end
```
**Better alternative:**
```elixir
defmodule MyApp.Repo do
@doc "Fetches a single record by primary key. Returns nil if not found."
def get(schema, id), do: ...
@doc "Fetches all records matching the query."
def all(query), do: ...
end
```
**Why:** `@doc false` means "this function is not part of the public API." If users are expected to call it, it needs documentation. Hiding public API behind `@doc false` is a maintenance hazard — users will call undocumented functions and break on upgrades.
---
## 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
```
### When to Use
**Triggers:**
- The module exists purely for internal code organization
- It's a protocol implementation module that users never reference directly
- It's a migration, task, or generated module that shouldn't appear in docs
**Example — before:**
```elixir
defmodule MyApp.Repo.Supervisor do
# Internal supervisor — users never interact with this directly
use Supervisor
def start_link(opts), do: ...
def init(opts), do: ...
end
```
**Example — after:**
```elixir
defmodule MyApp.Repo.Supervisor do
@moduledoc false
use Supervisor
def start_link(opts), do: ...
def init(opts), do: ...
end
```
### When NOT to Use
**Don't use this when:**
- The module has any function that users are expected to call
- You're hiding it because documentation feels like too much work
- The module is a behaviour that others will implement
**Over-application example:**
```elixir
defmodule MyApp.Telemetry do
@moduledoc false # "It's just telemetry setup, nobody cares"
def setup do
# Attaches telemetry handlers that affect app behavior
:telemetry.attach_many(...)
end
end
```
**Better alternative:**
```elixir
defmodule MyApp.Telemetry do
@moduledoc """
Telemetry event handlers for request metrics and error tracking.
Called from `Application.start/2`. Attaches handlers for:
- `[:my_app, :request, :stop]` — request duration histograms
- `[:my_app, :error]` — error counters
"""
def setup, do: ...
end
```
**Why:** If a module affects application behavior and other developers need to understand it during debugging or extension, it needs documentation. `@moduledoc false` should be reserved for truly mechanical/generated code.
---
## 7. Mermaid Diagrams in Documentation
**Source:** `lib/elixir/lib/gen_server.ex` lines 1420
**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
...
"""
```
### When to Use
**Triggers:**
- You're documenting a multi-component architecture (client-server, pipeline, state machine)
- The relationship between components is spatial or sequential and hard to express in prose
- ExDoc is your documentation renderer (it supports Mermaid natively)
**Example — before:**
```elixir
@moduledoc """
The pipeline processes events through three stages:
first the parser, then the transformer, then the loader.
The parser sends to transformer, transformer sends to loader.
Errors at any stage go to the error handler.
"""
```
**Example — after:**
```elixir
@moduledoc """
Event processing pipeline with three stages.
```mermaid
graph LR
Parser -->|events| Transformer
Transformer -->|records| Loader
Parser & Transformer & Loader -.->|errors| ErrorHandler
```
## Stages
...
"""
```
### When NOT to Use
**Don't use this when:**
- The module's architecture is simple enough that a one-sentence description suffices
- Your docs aren't rendered by a tool that supports Mermaid (raw markdown viewers won't render it)
- The diagram would be trivial (one box, one arrow) — a sentence is clearer
**Over-application example:**
```elixir
@moduledoc """
Wraps an integer counter.
```mermaid
graph LR
User -->|increment| Counter
Counter -->|value| User
```
"""
```
**Better alternative:**
```elixir
@moduledoc """
A simple integer counter. Call `increment/1` to add, `value/1` to read.
"""
```
**Why:** Diagrams add cognitive overhead. If the reader can understand the architecture faster from a sentence than from parsing a diagram, the diagram is noise. Reserve diagrams for genuinely complex interactions.
---
## 8. Admonition Blocks in Documentation
**Source:** `lib/elixir/lib/gen_server.ex` lines 8895, `lib/elixir/lib/supervisor.ex` lines 3438
**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.
...
"""
```
### When to Use
**Triggers:**
- There's critical information that users MUST notice (breaking changes, security implications, required setup)
- You need to document what `use MyModule` injects (the OTP convention)
- A common mistake or foot-gun deserves visual prominence
**Example — before:**
```elixir
@moduledoc """
...
Note: calling process/1 with untrusted input can execute arbitrary code.
Make sure to validate inputs first.
...
"""
```
**Example — after:**
```elixir
@moduledoc """
...
> #### Security Warning {: .warning}
>
> `process/1` evaluates its input. Never pass untrusted user input
> directly — always validate and sanitize first.
...
"""
```
### When NOT to Use
**Don't use this when:**
- The information isn't actually critical (don't cry wolf)
- You're using admonitions for every paragraph (they lose impact)
- Your rendering target doesn't support admonition syntax (it'll render as ugly blockquotes)
**Over-application example:**
```elixir
@moduledoc """
> #### Overview {: .info}
>
> This module provides helper functions.
> #### Usage {: .info}
>
> Call the functions with the appropriate arguments.
> #### Note {: .neutral}
>
> All functions return their results.
"""
```
**Better alternative:**
```elixir
@moduledoc """
Helper functions for string formatting.
## Usage
MyHelpers.format_name("alice") #=> "Alice"
"""
```
**Why:** Admonitions are visual interrupts — they break reading flow to demand attention. When everything is an admonition, nothing is. Reserve them for information where missing it would cause real problems.
---
## 9. @doc deprecated: Soft Deprecation
**Source:** `lib/elixir/lib/module.ex` lines 163180
**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
```
### When to Use
**Triggers:**
- You want to signal "prefer the alternative" without emitting compiler warnings
- The function still works fine but a better API exists
- You're planning a future hard deprecation and want to give users a migration window
**Example — before:**
```elixir
@doc "Fetches a user by ID. Deprecated: use Accounts.get_user/1."
def fetch_user(id), do: ...
```
**Example — after:**
```elixir
@doc deprecated: "Use Accounts.get_user/1 instead"
@doc "Fetches a user by ID."
def fetch_user(id), do: ...
```
### When NOT to Use
**Don't use this when:**
- You actually want compiler warnings (use `@deprecated "reason"` instead)
- The function is being removed in the next release (hard deprecation is appropriate)
- The function is still the recommended approach (don't pre-deprecate)
**Over-application example:**
```elixir
# Deprecating before the replacement exists
@doc deprecated: "Will be replaced by new_process/2 in v3.0"
def process(input) do
# new_process/2 doesn't exist yet!
...
end
```
**Better alternative:**
```elixir
# Wait until the replacement is available, then deprecate
def process(input), do: ...
# In v3.0, after new_process/2 ships:
@doc deprecated: "Use new_process/2 instead"
def process(input), do: new_process(input, [])
```
**Why:** Never deprecate a function before the recommended alternative exists and is documented. Users seeing "use X instead" need X to actually exist — otherwise you're creating confusion without offering a path forward.
---
## 10. Callback Documentation Convention
**Source:** `lib/elixir/lib/gen_server.ex` lines 584646 (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
```
### When to Use
**Triggers:**
- You're defining a behaviour that other modules will implement
- Implementers need to know: when the callback fires, what all return values mean, and which callbacks are optional
- The callback has multiple valid return shapes with different effects
**Example — before:**
```elixir
@callback handle_event(event :: term(), state :: term()) :: {:ok, term()} | {:error, term()}
```
**Example — after:**
```elixir
@doc """
Invoked when an event is received from the event bus.
`event` is the decoded event payload. `state` is the handler's
current state, initialized by `c:init/1`.
Returning `{:ok, new_state}` acknowledges the event and continues.
Returning `{:skip, new_state}` skips without acknowledgment (redelivery possible).
Returning `{:error, reason}` triggers the error handler configured in `start_link/1`.
This callback is required.
"""
@callback handle_event(event :: term(), state :: term()) ::
{:ok, new_state :: term()}
| {:skip, new_state :: term()}
| {:error, reason :: term()}
```
### When NOT to Use
**Don't use this when:**
- The callback has a single obvious return type and the name says it all
- You're documenting internal callbacks that aren't part of a public behaviour
- The behaviour is private to your application and only you implement it
**Over-application example:**
```elixir
@doc """
Called to format the value.
## Parameters
- `value` - The value to format (term)
## Returns
- `String.t()` - The formatted string
## Examples
iex> format(:hello)
"hello"
"""
@callback format(value :: term()) :: String.t()
```
**Better alternative:**
```elixir
@doc "Formats `value` into a human-readable string for display."
@callback format(value :: term()) :: String.t()
```
**Why:** A callback with one parameter and one return type doesn't need a full reference manual. Match documentation depth to complexity — a one-liner with good naming is better than padded sections that add no information.
---
## 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.
...
"""
```
### When to Use
**Triggers:**
- Your moduledoc or function doc references other functions, callbacks, or types in the same or other modules
- Users would benefit from clicking through to related documentation
- You have callbacks and functions with the same name that need disambiguation
**Example — before:**
```elixir
@doc """
Starts the server. Calls init/1 internally.
See the server type for accepted values.
"""
```
**Example — after:**
```elixir
@doc """
Starts the server. Calls `c:init/1` internally.
See `t:server/0` for accepted name values.
"""
```
### When NOT to Use
**Don't use this when:**
- You're writing documentation that won't be rendered by ExDoc (plain README, comments)
- The reference is to a well-known Erlang/Elixir function that readers will recognize without a link
- Over-linking makes the prose unreadable (every other word is a link)
**Over-application example:**
```elixir
@doc """
Returns `t:boolean/0` indicating if the `t:pid/0` from `Kernel.self/0`
matches the `t:pid/0` stored in `t:state/0` field `:owner`.
"""
```
**Better alternative:**
```elixir
@doc """
Returns `true` if the calling process is the owner of this resource.
"""
```
**Why:** Link references should aid navigation, not turn documentation into hypertext soup. Link types and callbacks that users might need to look up; don't link primitive types or universally known functions.