TL;DR

本文核心观点:

  1. 知识与代码同仓库 — 文档存在 Git 里,与代码共享版本历史,告别 Confluence/Notion 的同步地狱
  2. ADR 是决策的时间胶囊 — 每个架构决策记录”为什么这样选”,而不是”选了什么”,让后来者能看到完整的决策上下文
  3. PR 审核驱动知识演进 — 架构决策、编码规范、运行手册全部通过 PR 流程审查,知识库和代码库一起演进
  4. Key Insight — 知识不是消耗品,是基础设施;ADR 不是负担,是写给未来开发者的信

知识库即代码:把架构决策装进Git的时光机

2019年,某金融科技公司的架构师张工做出了一个重要决定:将核心支付系统从单体架构迁移到微服务。他写了一份详细的架构决策记录(ADR),保存在Confluence上。三年后,当团队发现这个决策导致了严重的性能问题时,张工已经离职,那份ADR淹没在Confluence的深处,无人能找。如果那份决策记录在Git里,与代码一起演进,历史会不会不同?


文档的墓地:为什么知识总是丢失

几乎每个技术团队都经历过这样的场景:

新员工:”为什么我们要用这个数据库?”

老员工:”呃…好像是三年前决定的,具体原因不清楚了。”

新员工:”那我们能换成其他的吗?”

老员工:”不知道,可能会出问题,建议别动。”

这就是知识的流失——不是技术能力的问题,而是记忆机制的失效

💡 Key Insight

知识应该与代码共存,知识应该有版本历史,知识应该通过PR流程演进。

知识流失的三个维度

1. 存储与代码分离

  • 文档在Confluence/Notion,代码在GitHub
  • 文档更新 ≠ 代码更新
  • 两个系统永远不同步

2. 版本不同步

  • 代码已经重构了10次,文档还是第一版
  • 新员工看着过时的文档,踩遍了已经修复的坑

3. 无法追溯决策过程

  • 只能看到”是什么”,看不到”为什么”
  • 重要决策的上下文(当时的约束、考虑的替代方案)全部丢失

一个真实的故事

某电商平台在2021年选择了Cassandra作为主要数据库。当时的架构师记录了决策原因:高写入吞吐量、分布式架构、与业务需求匹配。

2024年,业务模型发生了变化,读操作成为瓶颈。新团队考虑迁移到PostgreSQL,但:

  • 原始ADR在Notion的某个角落,搜索”Cassandra”找不到
  • 架构师已离职,无法询问
  • 团队不敢动,因为”不知道当初为什么选它”
  • 结果:在错误的数据库上堆砌了更多技术债务

如果ADR在Git里呢?

  • git log --grep="Cassandra" 立即找到所有相关决策
  • git blame 看到决策的完整上下文
  • git diff 对比当时和现在的约束条件

KBaaC:知识库即代码的范式

什么是KBaaC

Knowledge Base as Code(KBaaC) 是将组织知识(架构决策、编码规范、最佳实践)以代码形式管理的方法论。

核心理念

  • 知识应该与代码共存
  • 知识应该有版本历史
  • 知识应该通过PR流程演进

💡 Key Insight

每一个ADR都是一封写给未来开发者的信。

KBaaC vs 传统文档

维度 传统文档(Confluence/Notion) KBaaC(Git管理)
存储位置 独立系统 与代码同仓库
版本控制 手动版本历史 Git完整历史
更新流程 随时编辑 PR审核流程
可追溯性 有限 完整commit历史
搜索 关键词搜索 git log/grep/blame
权限管理 复杂 Git权限模型

KBaaC的三块基石

KBaaC 的知识体系由三大支柱构成:架构决策记录(ADR)、编码规范(Coding Standards)和运行手册(Runbooks)。这三者分别覆盖”做什么”(架构层)、”怎么做”(编码层)和”出了事怎么办”(运维层),共同构成一个完整的技术知识闭环。

1. 架构决策记录(ADR):记录团队做出的每一个重要架构决策——为什么选 PostgreSQL 而不是 MySQL,为什么引入消息队列而不是直接同步调用。ADR 的核心价值是保存决策的上下文,而不是结论本身。当三年后有人问”为什么要用这个技术”,ADR 能给出完整的来龙去脉,而不是”三年前某人拍脑袋决定的”。

2. 编码规范(Coding Standards):规定了代码怎么写、API 怎么设计、错误怎么处理。编码规范不是追求”最佳实践”的百科全书,而是”我们团队在这个项目里的共识”。它通过 PR 审查来执行——如果代码不符合规范,reviewer 有明确的依据可以要求修改,而不是说”我觉得这样不好”。

3. 运行手册(Runbooks):当系统出现故障时,runbook 是第一响应指南。它不是架构图或设计文档的重复,而是操作步骤的精确记录——数据库 failover 的具体命令、告警阈值是多少、哪个团队负责哪个服务的 on-call。runbook 的价值在故障发生时体现:当你在凌晨三点面对系统宕机,你需要的是精确的操作步骤,而不是”大概思路”。

KBaaC 三大支柱:ADR / 编码规范 / 运行手册

ADR:架构决策的标准格式

为什么需要标准化ADR格式

架构决策不是简单的”我们选择了X”,而是包含:

  • 当时的约束条件
  • 考虑的替代方案
  • 每个方案的权衡
  • 最终决策的理由
  • 预期的后果

💡 Key Insight

ADR 的价值不在于格式,而在于它强迫你写出”为什么这样选”——这个过程本身就是团队对齐认知最有效的手段。

ADR标准模板

Michael Nygard 在《Documenting Architecture Decisions》中定义的 ADR 模板是业界最广泛采用的格式,包含以下五个核心字段:

Title(标题):用一句话描述决策,例如 0003-使用PostgreSQL替代MongoDB作为主数据库。编号采用三位数字前缀,便于排序和引用。

Status(状态):表明决策的当前阶段。常见状态包括 Proposed(提议中)、Accepted(已接受)、Deprecated(已弃用)、Superseded(被取代)。状态随决策演进而更新,是团队判断某条规范是否仍有效的第一依据。

Context(上下文):描述做出这个决策时的背景——团队规模、技术栈、业务约束、时间压力。这部分的价值在于让后来者理解”为什么在当时的条件下,这个选择是合理的”。

Decision(决策):陈述最终的决定。不是简单的”选A不选B”,而是清晰地说明”我们决定做什么,以及这个决定的具体内容”。

Consequences(后果):诚实列出这个决策的正面和负面影响。承认代价比掩盖代价更有价值;后来者需要知道迁移到其他方案的难度和成本。

## ADR-0003:使用PostgreSQL替代MongoDB作为主数据库

**状态**:Accepted

**上下文**:...
**决策**:...
**后果**:...

💡 Key Insight

一个好的 ADR 不在于格式完美,而在于它忠实地记录了”当时的约束、考虑的替代方案、以及选择这个方案的真正理由”。

ADR的状态流转

ADR 从创建到归档会经历多个状态,每个状态的转变都有明确的触发条件和决策者。理解这个状态机,是把 ADR 从”写了就不管”变成”活文档”的关键。

Proposed(提议) 是起点:任何人发现了一个需要记录的技术决策,都可以起草 ADR,状态标记为 Proposed,然后提交 PR。这个阶段开放讨论,团队成员可以提出修改意见或替代方案。

Under Review(审核中) 发生在 PR 打开时:作者在 PR 描述中说明决策的背景和预期,reviewer 可以提出质疑。审核的门槛不是”所有人都同意”,而是”至少没有重大反对意见”——KBaaC 接受渐近式共识,而不是一票否决。

Accepted(已接受) 表示决策正式生效:PR 合入,ADR 状态更新为 Accepted,对应的代码变更也开始推进。这个状态下的 ADR 仍然是”活”的,如果外部条件发生变化(比如业务规模增长了 10 倍),可以提出新的 ADR 来重新审视这个决策。

Rejected(已拒绝) 是另一种可能的结局:如果团队审核后认为这个决策不值得推进,ADR 标记为 Rejected 并归档。已拒绝的 ADR 同样有参考价值——它记录了”我们考虑过但没有做”,防止团队在半年后重复踩坑。

Deprecated(已弃用)Superseded(被取代) 是 ADR 生命周期的后期状态:前者表示这个决策已经不再适用于当前场景,后者表示这个决策被新的 ADR 取代。两者都应该在文档中清楚说明原因和替代方案,并链接到新的 ADR 编号。

ADR 状态流转图

实施KBaaC:从0到1的路线图

基础设施建设

实施 KBaaC 的第一步不是写 ADR,而是把基础设施搭好。这一步的目标是让”写 ADR”变得和”写代码”一样自然——有目录可存、有模板可用、有流程可循。

1. 创建知识库目录结构:在项目根目录下新建 docs/ 目录,按类型组织三个子目录:docs/adr/ 存放所有架构决策记录,docs/standards/ 存放编码规范文档,docs/runbooks/ 存放运维操作手册。这个结构不需要复杂,但要全团队统一——统一的目录结构是后续自动化的前提。

2. 初始化 ADR 模板:在 docs/adr/ 目录下创建模板文件 TEMPLATE.md,采用 Michael Nygard 的五字段格式(Title、Status、Context、Decision、Consequences)。模板存放在仓库里,每次新建 ADR 时复制模板文件并重命名(采用 NNNN-title.md 编号格式)。这样既保证了格式一致,也不需要任何人记住格式细节。

3. 设置 Git 工作流:ADR 的提交遵循标准的 Git 流程——新建分支、编写内容、提交 PR、团队审查、合入主分支。与普通代码 PR 不同的是,ADR PR 的 reviewer 应该是”相关技术负责人”而非全量审查。可以配置 CODEOWNERS 文件,为 docs/adr/ 目录指定默认 reviewer,确保每个 ADR 都经过至少一个相关领域专家的审核。

试点项目验证

基础设施搭好后,不要急于全面推广。先选 1-2 个核心团队做试点,让 KBaaC 在小范围里先跑通,再逐步扩展。

1. 选择试点团队:试点团队应该是文档化意愿较高、技术决策较多的团队。选对了试点,第一批 ADR 的质量会更高,能为后续推广树立标杆。如果选了一个本来就不重视文档的团队,ADR 可能写了两周就停了,还会让整个组织觉得”KBaaC 没什么用”。

2. 迁移现有决策:从最近六个月的架构决策开始——这些决策的上下文还在团队记忆里,写 ADR 的成本最低。优先选择”高价值、高争议”的决策:要么是影响范围大的架构选择(如数据库选型、核心服务拆分),要么是当初团队有过激烈讨论的方案。写这些 ADR 的过程本身也是团队对齐认知的过程。

3. 建立审查流程:为 ADR 审查设计一个轻量级的 checklist:决策背景是否说清楚了?替代方案是否列出并说明了不选的原因?后果是否包含已知的负面影响?这个 checklist 不用很长,三到五条足够,目的是让 reviewer 有据可依,而不是凭感觉”看起来还行”。指定一名团队成员作为 ADR Steward,负责维护 ADR 模板、监督新 ADR 的提交质量、以及定期梳理过期的决策记录。

规模化与自动化

试点团队跑通之后,进入规模化推广阶段。这个阶段的核心任务是让 KBaaC 的维护成本降下来,让 ADR 数量增长而不增加管理负担。

1. CI/CD 集成:在 GitHub Actions 或 GitLab CI 中添加 ADR 验证步骤。常见检查包括:ADR 文件命名是否符合 NNNN-*.md 格式、Status 字段是否在白名单列表内、是否包含必需的五个字段(Title、Status、Context、Decision、Consequences)。CI 检查不通过则 PR 合不入,这比人工 review 更可靠,也更公平——规则面前人人平等,不会因为 reviewer 的心情或熟悉程度而有差异。

2. 自动化工具adr-tools 是一个命令行工具,支持用 adr new "决策标题" 快速创建 ADR、自动维护决策索引、以及生成分类报告。Log4brains 提供了 Web 界面,可以可视化地浏览 ADR 时间线、查看决策之间的关系图谱(哪些 ADR 被哪些 ADR 取代了)。两者可以组合使用:adr-tools 处理日常创建和管理,Log4brains 提供团队共享的查阅入口。

3. 知识图谱生成:当 ADR 数量超过几十条时,手动维护索引变得不现实。可以编写脚本扫描 docs/adr/ 目录,根据 Status 字段和日期自动生成 ADR 总览页面,并输出一个 JSON 格式的决策依赖图谱。这个图谱可以进一步集成到内部文档站点(如 MkDocs),让任何人都能快速了解”系统目前有哪些有效决策、哪些决策已经过期”。


KBaaC的最佳实践

实践一:ADR的粒度控制

不要:一个ADR包含多个独立决策

:每个ADR一个决策

实践二:及时记录

不要:决策实施后才补写ADR :决策的同时编写ADR,通过PR流程

工作流程

  1. 技术讨论 → 形成初步决策
  2. 编写ADR(草稿状态)
  3. 提交PR,团队审核
  4. 讨论修改 → 接受ADR
  5. 实施决策
  6. ADR与代码一起合并

实践三:链接相关决策

决策不是孤立的——每一个架构决策都有它的上下文和依赖关系。数据库选型会影响 API 设计,服务拆分会影响部署架构。如果 ADR 之间互相孤立,时间一长就变成了孤立的事实清单,没有人知道它们之间的关系。

链接策略有两种常见做法。第一种是标签系统:在 ADR 的元数据中添加 tags: [database, api-design] 这样的标签,查找时用标签聚合所有相关决策。第二种是显式引用:在新 ADR 的 Context 或 Consequences 段落中明确写出 参见 ADR-0017,形成决策依赖链。当某个 ADR 的状态更新为 Superseded 时,必须在新 ADR 中说明”本决策取代了 ADR-XXXX”,并在旧 ADR 的 Status 中注明”被 ADR-YYYY 取代”。这种双向链接让后来者既能看到最新结论,也能追溯历史上下文。

实践四:处理决策变更

决策会过时,这是正常的——接受这一点,是让 KBaaC 可持续运转的前提。试图让每一个 ADR 永远准确,是不可能完成的任务;接受”过时是正常的,有记录可查才是重点”,才能让团队持续写 ADR。

处理决策变更的正确流程是:不要删除或覆盖旧的 ADR,而是写一条新的 ADR 来说明变更。假设 ADR-0017 决定使用 Redis 作为缓存,三年后业务规模增长了 10 倍,Redis 不再是合适的选择——正确的做法是写 ADR-0042,说明”由于业务规模扩大,我们决定用本地缓存替代 Redis”,并在 ADR-0042 的 Supersedes 字段中写明 ADR-0017。ADR-0017 不会被删除,它的状态更新为 Superseded,但仍然保留在仓库里,作为历史的完整记录。

实践五:与代码审查结合

代码审查时,同时检查相关的 ADR——代码变更和架构决策应该是同一件事的两面。在 PR 模板中添加一个强制填写字段:本 PR 是否涉及新的架构决策?若是,请提供对应的 ADR 编号。这个字段不需要复杂,但它创造了一个”决策必须被显式记录”的机制。

具体操作流程:reviewer 在审阅代码时,如果发现这个 PR 引入了一个新的架构选择(比如引入了新的外部依赖、改变了数据存储方式、或者删除了某个被认为”不需要”的功能),就在 PR 评论中标记”这看起来是一个架构决策,需要对应的 ADR”。作者收到反馈后,补充 ADR 并将编号填入 PR 模板,reviewer 才最终 approve。用 CODEOWNERSdocs/adr/ 目录配置专门的 knowledge-base 团队,这个团队的成员会自动被邀请审阅所有涉及 docs/ 目录变更的 PR,确保 ADR 的质量。


工具与生态系统

ADR工具

adr-tools:命令行ADR管理工具

Log4brains:现代化的ADR管理工具

  • Web界面浏览ADR
  • 自动生成决策时间线
  • 集成Git历史

文档工具

MkDocs:静态文档站点生成器

Obsidian + Git:个人知识管理

  • 本地Markdown编辑
  • Git同步
  • 双向链接

集成工具

GitHub/GitLab

  • PR模板强制ADR检查
  • CODEOWNERS指定知识库审核人
  • 自动化的ADR验证CI

Slack/Discord机器人

  • 新ADR提交时通知团队
  • 定期提醒过期的ADR审查

KBaaC的挑战与解决方案

挑战一:维护成本

问题:写ADR需要额外时间,团队可能抵触。

  • 从最重要的决策开始(二八法则)
  • 将ADR编写纳入迭代计划(算入工时)
  • 展示长期收益(减少重复讨论、加速新人入职)

挑战二:决策过时

问题:ADR快速过时,维护困难。

  • 定期审查(每季度ADR梳理会议)
  • 明确标记过时ADR
  • 接受”过时是正常的”,重点是有记录可查

挑战三:搜索与发现

问题:ADR多了之后,难以找到相关信息。

  • 良好的命名规范
  • 标签系统
  • 自动生成索引和图谱
  • 集成搜索工具(如Algolia)

💡 Key Insight

KBaaC 的挑战不是技术问题,是习惯问题——让团队愿意写 ADR,比让 ADR 系统跑起来要难得多。


结语:给未来的时间胶囊

回到张工的故事。

如果他在2019年使用了KBaaC:

KBaaC不是增加工作负担,而是给未来的团队留下时间胶囊。

每一个ADR都是一封写给未来开发者的信:

  • “我们当时面临这样的问题…”
  • “我们考虑过这些选项…”
  • “我们最终选择了这个,原因是…”
  • “如果你要推翻这个决策,这些是当时的约束…”

在这个知识快速流失的行业,KBaaC是我们对抗遗忘的武器。


参考资源


深度阅读时间:约 17 分钟

*Published on 2025-03-03 阅读时间:约 15 分钟*

💡 Key Insight

知识不是消耗品,是基础设施——它不是用完就没了,而是被用得越多越有价值。ADR 是给未来开发者的礼物,也是给过去决策者的尊重。