Chapter 08

文档自动化:注释与 README

让 AI 承担文档编写的繁重工作,保持代码与文档始终同步

AI 生成注释的策略

什么时候需要注释?

一个常见的误区是"注释越多越好"。实际上,好的代码应该是自解释的,注释应该解释为什么(Why),而不是是什么(What)。

需要注释的情况

  • 复杂的业务逻辑(解释业务规则来源)
  • 非直觉的算法(解释为什么选择这个方法)
  • 临时的 workaround(说明原因和何时可以删除)
  • 公共 API 函数(所有参数和返回值)

不需要注释的情况

  • 自解释的代码(i++ 不需要 // i 加一
  • 可以用更好的命名替代的注释
  • 重复代码语义的注释
  • 过时的注释(比没有注释更危险)

函数文档注释自动生成

# 给 AI 的提示:为以下 Python 函数生成 Google Style docstring
async def send_notification(
    user_ids: list[int],
    message: str,
    channel: str = "email",
    priority: int = 5
) -> dict[str, int]:
    # AI 生成的 Google Style docstring
    """向指定用户发送通知。

    通过配置的通知渠道(邮件/短信/Push)向用户列表发送通知消息。
    会自动过滤无效的用户 ID 和退订用户。

    Args:
        user_ids: 接收通知的用户 ID 列表。重复的 ID 只发送一次。
        message: 通知内容,支持 Markdown 格式(仅在 email 渠道有效)。
            长度限制:email 不超过 10000 字符,sms 不超过 160 字符。
        channel: 通知渠道,支持 'email'、'sms'、'push'。
            默认为 'email'。
        priority: 发送优先级,1(最高)到 10(最低),默认 5。
            高优先级(1-3)会绕过频率限制直接发送。

    Returns:
        发送结果统计字典:
        {
            'sent': 成功发送数量,
            'failed': 发送失败数量,
            'filtered': 被过滤数量(无效ID/退订)
        }

    Raises:
        ValueError: 当 channel 不是支持的渠道类型时。
        ValueError: 当 priority 不在 1-10 范围内时。
        RateLimitError: 当发送频率超过限制时(仅低优先级触发)。

    Example:
        >>> result = await send_notification(
        ...     user_ids=[1, 2, 3],
        ...     message="您的订单已发货",
        ...     channel="sms"
        ... )
        >>> print(result)
        {'sent': 3, 'failed': 0, 'filtered': 0}
    """

TypeScript/JavaScript JSDoc 生成

// AI 生成的 JSDoc
/**
 * 计算购物车的总价,包含税费和折扣。
 *
 * @param items - 购物车商品列表
 * @param coupon - 优惠券对象,可选
 * @param taxRate - 税率(0-1 之间的小数),默认 0.1(10%)
 * @returns 价格计算结果对象
 *
 * @example
 * ```typescript
 * const items = [
 *   { id: 1, price: 29.99, quantity: 2 },
 *   { id: 2, price: 9.99, quantity: 1 }
 * ];
 * const result = calculateCartTotal(items, null, 0.08);
 * console.log(result.total); // 75.87
 * ```
 *
 * @throws {Error} 当 taxRate 不在 0-1 范围内时
 */
function calculateCartTotal(
  items: CartItem[],
  coupon: Coupon | null,
  taxRate: number = 0.1
): PriceResult { ... }

Rust Doc Comments 生成

/// 解析 TOML 配置文件并返回强类型配置结构。
///
/// 会按照以下优先级合并配置(高优先级覆盖低优先级):
/// 1. 环境变量(前缀 `APP_`)
/// 2. 用户配置文件(`~/.config/app/config.toml`)
/// 3. 默认配置
///
/// # Arguments
///
/// * `path` - 配置文件路径,如果文件不存在则使用默认配置
///
/// # Returns
///
/// 返回 [`AppConfig`] 结构,所有字段均已填充(有默认值保底)
///
/// # Errors
///
/// - [`ConfigError::ParseError`] - TOML 语法错误
/// - [`ConfigError::ValidationError`] - 配置值不合法(如端口超出范围)
///
/// # Examples
///
/// ```rust
/// use myapp::config::load_config;
///
/// let config = load_config("config.toml")?;
/// assert_eq!(config.server.port, 8080);
/// ```
pub fn load_config(path: &Path) -> Result<AppConfig, ConfigError> {
    todo!()
}

README 自动生成

README 生成 Prompt 模板

请根据以下项目信息生成一个专业的 README.md。

项目基本信息:
- 项目名称:{project_name}
- 一句话描述:{description}
- 技术栈:{tech_stack}

请生成包含以下部分的 README:

1. **项目标题和徽章**(build status, coverage, license, version)
2. **简介**(2-3 段,说明项目解决什么问题,主要特性)
3. **快速开始**(5分钟内能跑起来的最短步骤)
4. **安装**(详细的安装步骤,包括依赖)
5. **配置**(重要的配置项说明,.env 示例)
6. **使用方法**(基本使用示例,配代码块)
7. **API 文档**(如果是库/服务,列出主要接口)
8. **开发指南**(如何在本地开发、运行测试)
9. **部署**(如何部署到生产环境)
10. **贡献指南**(简单说明 PR 流程)
11. **License**

格式要求:
- 使用清晰的标题层次
- 代码示例使用代码块并标注语言
- 关键命令用代码格式
- 中文文档(除了技术术语)

项目目录结构:
{directory_tree}

package.json / pyproject.toml 内容:
{config_file}

自动化 README 更新脚本

#!/usr/bin/env python3
# scripts/update_readme.py
# 在 CI/CD 中运行,自动更新 README 中的 API 文档部分

import subprocess
import anthropic
import re


def get_api_changes() -> str:
    """获取最近的 API 相关变更"""
    result = subprocess.run(
        ["git", "diff", "HEAD~1", "HEAD", "--", "src/api/"],
        capture_output=True, text=True
    )
    return result.stdout


def update_readme_api_section(changes: str) -> None:
    client = anthropic.Anthropic()

    with open("README.md", "r") as f:
        readme = f.read()

    response = client.messages.create(
        model="claude-3-5-haiku-20241022",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": f"""
根据以下 API 代码变更,更新 README.md 中的 API 文档部分。
只更新 ## API 文档 和 ## 快速开始 部分。

代码变更:
{changes}

当前 README:
{readme}

请输出完整的更新后的 README.md 内容。
"""
        }]
    )

    with open("README.md", "w") as f:
        f.write(response.content[0].text)


if __name__ == "__main__":
    changes = get_api_changes()
    if changes:
        update_readme_api_section(changes)

Changelog 自动生成

从 Git Log 生成 Changelog

# 获取上一个版本以来的 commit 历史
git log v1.2.0..HEAD --pretty=format:"%h %s (%an)" --no-merges

# 输出示例:
# a3f8b21 feat: add user avatar upload endpoint (Alice)
# 8d2c1b3 fix: prevent duplicate email registration (Bob)
# 1e9a4f5 refactor: extract payment service (Alice)
# 7c3b2d0 docs: update API documentation (Carol)
# 给 AI 的 Changelog 生成 Prompt

请根据以下 git commit 历史生成用户友好的 Changelog。

版本号:{new_version}
发布日期:{release_date}
上一版本:{previous_version}

Commit 历史:
{git_log}

生成规则:
1. 按类型分组:Breaking Changes、New Features、Bug Fixes、Performance、Refactoring、Documentation
2. Breaking Changes 必须放在最前面并用警告标记
3. 使用用户视角的语言("现在可以上传头像"而非"添加了 avatar upload endpoint")
4. 过滤掉纯内部重构(用户不关心的变更)
5. 遵循 Keep a Changelog 格式(https://keepachangelog.com)

输出格式:Markdown

OpenAPI Spec 文档生成与同步

从代码自动生成 OpenAPI

# FastAPI 自动生成 OpenAPI Spec
# FastAPI 本身支持自动生成,但我们可以用 AI 增强注释质量

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(
    title="My API",
    description="""
## 概述
这是 MyApp 的后端 API 文档。

## 认证
所有端点(除了 `/auth/login`)都需要在 Header 中提供 Bearer Token:
```
Authorization: Bearer {your_token}
```

## 错误码
| 状态码 | 含义 |
|--------|------|
| 400 | 请求参数错误 |
| 401 | 未认证或 Token 过期 |
| 403 | 权限不足 |
| 404 | 资源不存在 |
| 429 | 请求频率超限 |
    """,
    version="1.0.0"
)


class UserCreateRequest(BaseModel):
    email: str = Field(
        ...,
        description="用户邮箱,注册后不可修改",
        example="user@example.com",
        pattern=r"^[^@]+@[^@]+\.[^@]+$"
    )
    name: str = Field(
        ...,
        min_length=1,
        max_length=50,
        description="显示名称,长度 1-50 字符",
        example="张三"
    )
文档与代码同步的最佳实践

最糟糕的文档是过时的文档——它比没有文档更危险,因为它会误导用户。建议在 CI/CD 流程中添加文档同步检查:每次 PR 合并时,自动更新 API 文档,并用 AI 检查 README 中的示例代码是否仍然有效。

文档自动化的深层原则

文档腐化(Documentation Rot)的根本原因

文档变得过时,几乎是每个项目的必然问题。理解根本原因才能设计正确的防腐策略:

文档和代码的"双写"成本
修改函数时,开发者需要同时更新文档字符串、README 示例、API 文档——额外的工作很容易被"先跑通功能"的优先级压后。AI 能将文档生成自动化,但更重要的是将"代码改了→文档自动提醒更新"纳入工作流(如 CI 检查 docstring 与函数签名的一致性)。
隐式知识未文档化
开发者知道"这个函数不能并发调用"、"传入的 ID 必须先被 validate_id() 检查过",但这些隐式约束往往不写在文档里,因为"自己知道"。AI 生成文档时,应明确要求 AI 检查"是否有任何调用前置条件或副作用应该记录",将隐式知识显式化。
文档粒度不当
为每行代码写注释(过细)和只有一个顶层 README(过粗)都是文档问题。正确粒度:每个公共函数有 docstring(谁调用谁需要知道),每个模块有模块级注释(描述这个模块的职责),项目有 README(快速上手),架构有 ADR(Architecture Decision Record,记录重要决策的背景和理由)。

ADR(架构决策记录)与 AI 的结合

架构决策记录(ADR)是文档化"为什么做了这个技术决策"的最佳实践。AI 可以帮助起草 ADR:

# ADR-003:选择 PostgreSQL 而非 MongoDB

## 状态
已接受

## 背景
系统需要存储用户订单数据。订单数据结构相对固定(有明确的关系),但未来可能增加灵活的扩展字段。

## 决策
使用 PostgreSQL + JSONB 字段。主要结构使用关系表,扩展属性存入 JSONB 列。

## 原因
- 订单数据有严格的 ACID 事务需求(不能部分写入)
- 现有团队的 SQL 经验更丰富
- PostgreSQL 的 JSONB 提供了足够的灵活性
- 避免引入新的基础设施(当前已有 PostgreSQL 实例)

## 后果
- 正面:事务安全、运维成本低、学习成本低
- 负面:扩展属性的查询性能不如 MongoDB;如果数据结构变化频繁需要数据库迁移
- 中性:需要注意 JSONB 列不能建立标准索引(需要 GIN 索引)
让 AI 帮助生成 ADR 的正确方式

向 AI 提供:你们讨论的技术选型背景、考虑过的备选方案、最终选择的理由(口语描述即可)。AI 帮你整理成标准 ADR 格式。关键:ADR 中的"原因"和"后果"必须是你真实思考的结果,不能让 AI 编造——否则 ADR 就失去了"记录真实决策依据"的价值。

文档自动化工具链

pdoc3 / Sphinx(Python)
从代码中的 docstring 自动生成 HTML 文档。pdoc3 更简单(零配置),Sphinx 更强大(支持交叉引用、自定义主题)。配合 AI 生成的 docstring,几乎可以实现"代码即文档"。
TypeDoc(TypeScript)
从 TypeScript 源码的 JSDoc 注释生成文档。TypeScript 的类型信息会自动反映在文档中(参数类型、返回类型无需重复说明)。
Storybook(UI 组件文档)
React/Vue/Svelte 组件的交互式文档系统。AI 可以帮助生成 Story 文件(组件的各种使用场景展示),让 UI 文档和组件代码保持同步。
swagger-autogen / FastAPI 自动文档
FastAPI 基于 Python 类型注解自动生成 OpenAPI 文档,是"代码即文档"的最佳实践。AI 生成的 Pydantic Model 和路由函数,只要类型注解完整,API 文档就自动完整。
第8章小结