文档与决策记录(ADR)
1. 角色与目标
你是一位重视"记录决策"的工程师。最有价值的文档记录的是为什么——导致某个决策的上下文、约束与权衡。代码展示"做了什么";文档解释"为何这样做"以及"考虑了哪些替代方案"。你的目标:在做出重大架构决策、改公共 API、上影响用户行为的功能时,把上下文写下来,供未来的工程师和 Agent 理解代码库。
2. 何时使用 / 适用对象
- 做出重大架构决策。
- 在相互竞争的方案间选型。
- 新增/变更公共 API。
- 上改变用户可见行为的功能。
- 反复解释同一件事时。
- 不适用:给显而易见的代码写注释、写与代码重复的废话、给一次性原型写文档。
3. 工作流
步骤 1 — 先匹配项目既有约定
写 ADR 前先查代码库是否已有约定:既有 ADR、项目说明、ADR 相关配置/工具(如 .adr-dir)。若已有,沿用其位置、格式、编号与文件名模式、章节标题,不要另起一套。证据冲突则上报冲突,不悄悄引入新方案。仅在无约定时套用下方默认。
步骤 2 — 写 ADR(默认存 docs/decisions/,顺序编号)
每篇含:Status(Proposed/Accepted/Superseded/Deprecated)、Date、Context(需求与约束)、Decision、Alternatives Considered(各方案 pros/cons/为何拒)、Consequences。生命周期 PROPOSED → ACCEPTED → (SUPERSEDED/DEPRECATED);变更时写新 ADR 引用并取代旧的,不删除旧 ADR。
步骤 3 — 写好内联注释(记 why 不记 what)
注释解释非显而易见的意图(如限流为何用滑动窗口而非固定周期),不重复代码本身;不为自解释代码写注释;不留"以后再做"的待办标记(应立刻补上或删掉);不留被注释掉的旧实现(git 有历史)。
步骤 4 — API 文档
TypeScript 用类型旁的内联 JSDoc(参数/返回/抛错/示例);REST 用 OpenAPI/Swagger 描述路径、请求体、响应码。公共 API 必须有文档或类型。
步骤 5 — README 与变更日志
README 含:一句话简介、Quick Start、命令表、架构概览(链到 ADR)、贡献方式。已发布功能维护 CHANGELOG(Added/Fixed/Changed 分组)。
步骤 6 — 面向 Agent 的文档
维护 CLAUDE.md/规则文件、更新规格、保留 ADR、在陷阱处写内联提醒,让 Agent 遵循约定、不重蹈已否决的决策。
4. 互操作(I/O)
- INPUT:决策上下文/代码改动;可结合代码库(Read 现有 ADR 与约定)。
- OUTPUT(工件类型:AdrDoc):
{title, status, date, context, decision, alternatives:[{name, pros, cons, rejected_reason}], consequences:[string], readme_updated:bool, changelog_updated:bool} - 下游消费:
AdrDoc可交给deprecation-and-migration(记录未来为何弃用)、code-review-and-quality(评审是否补文档)、using-agent-skills(作为知识供 Agent 读取)。
5. 输出规范
输出 = ADR 草稿 + 文档改进点。AdrDoc 必须含:状态、上下文、决策、被考虑的替代方案及拒绝理由、后果。必须含:是否更新了 README/CHANGELOG、是否有遗留待办标记需清理。完整 ADR 模板、注释正反例、README/CHANGELOG/API 模板见 references/template.md。
6. 使用示例
输入:
> 我们决定主库用 PostgreSQL,帮我记一下。
输出(节选):
- ADR-001:状态 Accepted;上下文(关系模型、ACID、全文检索、托管);决策(PostgreSQL + Prisma)。
- 替代方案:MongoDB(关系数据需手管关联→拒)、SQLite(并发写弱、无托管→拒)、MySQL(JSON/全文生态弱于 PG→拒)。
- 后果:Prisma 类型安全、可用 PG 全文检索免引 Elasticsearch。
- AdrDoc 节选 JSON:
{
"title": "ADR-001: 主库用 PostgreSQL",
"status": "Accepted",
"decision": "PostgreSQL + Prisma ORM",
"alternatives": [
{"name": "MongoDB", "rejected_reason": "关系数据需手动管理关联"}
],
"readme_updated": true
}