elixir-writing-docs

Doctests: syntax, gotchas, when to use references/doctests.md

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "elixir-writing-docs" with this command: npx skills add existential-birds/beagle/existential-birds-beagle-elixir-writing-docs

Elixir Writing Docs

Quick Reference

Topic Reference

Doctests: syntax, gotchas, when to use references/doctests.md

Cross-references and linking syntax references/cross-references.md

Admonitions, formatting, tabs references/admonitions-and-formatting.md

First-Line Summary Rule

ExDoc and tools like mix docs extract the first paragraph of @moduledoc and @doc as a summary. Keep the opening line concise and self-contained.

GOOD - first line works as a standalone summary

@moduledoc """ Handles payment processing through Stripe and local ledger reconciliation.

Wraps the Stripe API client and ensures each charge is recorded in the local ledger before returning a confirmation to the caller. """

BAD - first line is vague, forces reader to continue

@moduledoc """ This module contains various functions related to payments.

It uses Stripe and also updates the ledger. """

The same rule applies to @doc :

GOOD

@doc """ Charges a customer's default payment method for the given amount in cents.

Returns {:ok, charge} on success or {:error, reason} when the payment gateway rejects the request. """

BAD

@doc """ This function is used to charge a customer. """

@moduledoc Structure

A well-structured @moduledoc follows this pattern:

defmodule MyApp.Inventory do @moduledoc """ Tracks warehouse stock levels and triggers replenishment orders.

This module maintains an ETS-backed cache of current quantities and exposes functions for atomic stock adjustments. It is designed to be started under a supervisor and will restore state from the database on init.

Examples

  iex> {:ok, pid} = MyApp.Inventory.start_link(warehouse: :east)
  iex> MyApp.Inventory.current_stock(pid, "SKU-1042")
  {:ok, 350}

Configuration

Expects the following in config/runtime.exs:

  config :my_app, MyApp.Inventory,
    repo: MyApp.Repo,
    low_stock_threshold: 50

""" end

Key points:

  • First paragraph is the summary (one to two sentences).

  • Examples

shows realistic usage. Use doctests when the example is runnable.

  • Configuration

documents required config keys. Omit this section if the module takes no config.

  • Use second-level headings (## ) only. First-level (# ) is reserved for the module name in ExDoc output.

Documenting Behaviour Modules

When defining a behaviour, document the expected callbacks:

defmodule MyApp.PaymentGateway do @moduledoc """ Behaviour for payment gateway integrations.

Implementations must handle charging, refunding, and status checks. See MyApp.PaymentGateway.Stripe for a reference implementation.

Callbacks

  • charge/2 - Initiate a charge for a given amount
  • refund/2 - Refund a previously completed charge
  • status/1 - Check the status of a transaction """

@callback charge(amount :: pos_integer(), currency :: atom()) :: {:ok, transaction_id :: String.t()} | {:error, term()}

@callback refund(transaction_id :: String.t(), amount :: pos_integer()) :: :ok | {:error, term()}

@callback status(transaction_id :: String.t()) :: {:pending | :completed | :failed, map()} end

@doc Structure

@doc """ Reserves the given quantity of an item, decrementing available stock.

Returns {:ok, reservation_id} when stock is available, or {:error, :insufficient_stock} when the requested quantity exceeds what is on hand.

Examples

iex> MyApp.Inventory.reserve("SKU-1042", 5)
{:ok, "res_abc123"}

iex> MyApp.Inventory.reserve("SKU-9999", 1)
{:error, :not_found}

Options

  • :warehouse - Target warehouse atom. Defaults to :primary.
  • :timeout - Timeout in milliseconds. Defaults to 5_000. """ @spec reserve(String.t(), pos_integer(), keyword()) :: {:ok, String.t()} | {:error, :insufficient_stock | :not_found} def reserve(sku, quantity, opts \ []) do

...

end

Guidelines:

  • State what the function does, then what it returns.

  • Document each option in a bulleted ## Options section when the function accepts a keyword list.

  • Place @spec between @doc and def . This is the conventional ordering.

  • Include doctests for pure functions. Skip them for side-effecting functions (see references/doctests.md).

@typedoc

Document custom types defined with @type or @opaque :

@typedoc """ A positive integer representing an amount in the smallest currency unit (e.g., cents). """ @type amount :: pos_integer()

@typedoc """ Reservation status returned by status/1.

  • :held - Stock is reserved but not yet shipped
  • :released - Reservation was cancelled and stock restored
  • :fulfilled - Items have shipped """ @type reservation_status :: :held | :released | :fulfilled

@typedoc """ Opaque handle returned by connect/1. Do not pattern-match on this value. """ @opaque connection :: %MODULE{socket: port(), buffer: binary()}

For @opaque types, the @typedoc is especially important because callers cannot inspect the structure.

Metadata

@doc since and @doc deprecated

@doc since: "1.3.0" @doc """ Transfers stock between two warehouses. """ def transfer(from, to, sku, quantity), do: # ...

@doc deprecated: "Use transfer/4 instead" @doc """ Moves items between locations. Deprecated in favor of transfer/4 which supports cross-region transfers. """ def move_stock(from, to, sku, quantity), do: # ...

You can combine metadata and the docstring in one attribute:

@doc since: "2.0.0", deprecated: "Use bulk_reserve/2 instead" @doc """ Reserves multiple items in a single call. """ def batch_reserve(items), do: # ...

@moduledoc since: works the same way for modules:

@moduledoc since: "1.2.0" @moduledoc """ Handles webhook signature verification for Stripe events. """

When to Use @doc false / @moduledoc false

Suppress documentation when the module or function is not part of the public API:

Private implementation module — internal to the application

defmodule MyApp.Inventory.StockCache do @moduledoc false

...

end

Protocol implementation — documented at the protocol level

defimpl String.Chars, for: MyApp.Money do @moduledoc false

...

end

Callback implementation — documented at the behaviour level

@doc false def handle_info(:refresh, state) do

...

end

Helper used only inside the module

@doc false def do_format(value), do: # ...

Do NOT use @doc false on genuinely public functions. If a function is exported and callers depend on it, document it. If it should not be called externally, make it private with defp .

Documentation vs Code Comments

Documentation (@moduledoc , @doc ) Code Comments (# )

Audience Users of your API Developers reading source

Purpose Contract: what it does, what it returns Why a particular implementation choice was made

Rendered Yes, by ExDoc in HTML/epub No, visible only in source

Required All public modules and functions Only where code intent is non-obvious

@doc """ Validates that the given coupon code is active and has remaining uses. """ @spec validate_coupon(String.t()) :: {:ok, Coupon.t()} | {:error, :expired | :exhausted} def validate_coupon(code) do

We query the read replica here to avoid adding load to the

primary during high-traffic discount events.

Repo.replica().get_by(Coupon, code: code) |> check_expiry() |> check_remaining_uses() end

The @doc tells the caller what validate_coupon/1 does and returns. The inline comment explains an implementation decision that would otherwise be surprising.

When to Load References

  • Writing doctests or debugging doctest failures --> references/doctests.md

  • Adding links between modules, functions, types --> references/cross-references.md

  • Using admonition blocks, tabs, or formatting in docs --> references/admonitions-and-formatting.md

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

tailwind-v4

No summary provided by upstream source.

Repository SourceNeeds Review
General

react-flow

No summary provided by upstream source.

Repository SourceNeeds Review
General

react-router-v7

No summary provided by upstream source.

Repository SourceNeeds Review
General

vitest-testing

No summary provided by upstream source.

Repository SourceNeeds Review