知识库即代码:把架构决策装进Git的时光机
TL;DR
本文核心观点:
- 知识与代码同仓库 — 文档存在 Git 里,与代码共享版本历史,告别 Confluence/Notion 的同步地狱
- ADR 是决策的时间胶囊 — 每个架构决策记录”为什么这样选”,而不是”选了什么”,让后来者能看到完整的决策上下文
- PR 审核驱动知识演进 — 架构决策、编码规范、运行手册全部通过 PR 流程审查,知识库和代码库一起演进
- 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 的价值在故障发生时体现:当你在凌晨三点面对系统宕机,你需要的是精确的操作步骤,而不是”大概思路”。
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 编号。
实施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流程
工作流程:
- 技术讨论 → 形成初步决策
- 编写ADR(草稿状态)
- 提交PR,团队审核
- 讨论修改 → 接受ADR
- 实施决策
- 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。用 CODEOWNERS 为 docs/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是我们对抗遗忘的武器。
参考资源
- ADR GitHub Organization
- Documenting Architecture Decisions - Michael Nygard
- Log4brains - ADR Management Tool
- Architecture Decision Records - AWS Prescriptive Guidance
深度阅读时间:约 17 分钟
| *Published on 2025-03-03 | 阅读时间:约 15 分钟* |
💡 Key Insight
知识不是消耗品,是基础设施——它不是用完就没了,而是被用得越多越有价值。ADR 是给未来开发者的礼物,也是给过去决策者的尊重。
💬 评论
💡 使用 GitHub 账号登录 即可参与讨论