关于 elixir-writing-docs
面向 Elixir 开发者的文档编写 Skill,依据 ExDoc 约定讲解文档字符串、doctest、交叉引用与格式。帮助为模块和公开函数组织并检查文档。
主要功能
- 01首段摘要编写规则
- 02模块与函数文档结构
- 03doctest 与交叉引用指引
- 04格式与提示块参考
- 05文档完成检查
适用场景
- 01撰写简洁的 @moduledoc 和 @doc
- 02为 API 文档添加示例与 doctest
- 03检查公开 API 的文档覆盖情况
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ existential-birds/ beagle --skill elixir-writing-docs安装后在 Claude Code 中输入/elixir-writing-docs确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/admonitions-and-formatting.mdcross-references.mddoctests.md详细介绍
Elixir 文档编写
快速参考
| 主题 | 参考资料 |
|---|---|
| Doctest:语法、注意事项、适用场景 | references/doctests.md |
| 交叉引用和链接语法 | references/cross-references.md |
| 提示框、格式设置、选项卡 | references/admonitions-and-formatting.md |
首行摘要规则
ExDoc 和 mix docs 等工具会将 @moduledoc 和 @doc 的首段提取为摘要。开头一行应简洁明了、内容完整。
# 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.
"""
@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 结构
结构良好的 @moduledoc 应遵循以下模式:
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
要点:
- 第一段是摘要(一到两句话)。
## Examples展示真实的用法。示例可运行时,使用 doctest。## Configuration说明必需的配置键。如果模块不需要配置,则省略此部分。- 仅使用二级标题(
##)。一级标题(#)保留给 ExDoc 输出中的模块名称。
编写行为模块文档
定义行为时,应记录预期的回调:
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 结构
@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
指南:
- 说明函数的功能,然后说明其返回值。
- 函数接受关键字列表时,在项目符号列表形式的
## Options部分中说明每个选项。 - 将
@spec放在@doc和def之间。这是惯用的顺序。 - 为纯函数添加 doctest。对于有副作用的函数则跳过(参见 references/doctests.md)。
@typedoc
为使用 @type 或 @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()}
对于 @opaque 类型,@typedoc 尤其重要,因为调用方无法检查其结构。
元数据
@doc since 和 @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: # ...
你可以将元数据和文档字符串合并到同一个属性中:
@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: 对模块的作用方式相同:
@moduledoc since: "1.2.0"
@moduledoc """
Handles webhook signature verification for Stripe events.
"""
何时使用 @doc false / @moduledoc false
当模块或函数不属于公共 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: # ...
切勿对真正的公共函数使用 @doc false。 如果函数已导出且调用方依赖它,就应为其编写文档。如果不应从外部调用该函数,则将其设为私有函数,使用 defp。
文档与代码注释
文档(@moduledoc、@doc) | 代码注释(#) | |
|---|---|---|
| 读者 | API 的使用者 | 阅读源代码的开发者 |
| 目的 | 约定:功能及返回值 | 解释某项特定实现选择的原因 |
| 呈现方式 | 是,由 ExDoc 以 HTML/epub 呈现 | 否,仅在源代码中可见 |
| 要求 | 所有公共模块和函数 | 仅在代码意图不明显时添加 |
@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
@doc 会告诉调用方 validate_coupon/1 的功能和返回值。内联注释则解释一项可能令人意外的实现决策。
完成文档编写(检查关卡)
最后按顺序完成以下检查。不适用的步骤可以跳过。
- 添加或修改了 Doctest? 运行项目的 doctest 验证(通常是针对受影响模块运行
mix test,或运行完整测试套件)。**通过标准:**没有 doctest 失败。 - 添加或编辑了交叉引用、反引号或
m:链接? 运行mix docs。**通过标准:**命令顺利完成;解决 ExDoc 关于缺少模块、回调或错误链接的警告。 - 新增或修改了公共 API? **通过标准:**每个导出的
def/defmacro都有明确的@doc或@doc false,且每个公共模块都有@moduledoc或@moduledoc false,并符合项目的策略。
何时加载参考资料
- 编写 doctest 或调试 doctest 失败问题 --> references/doctests.md
- 在模块、函数和类型之间添加链接 --> references/cross-references.md
- 在文档中使用警示块、标签页或格式设置 --> references/admonitions-and-formatting.md
评论
支持 Markdown