架构决策记录:用 ADR 追踪每一个关键技术选型
TL;DR
架构决策记录:用 ADR 追踪每一个关键技术选型
“系列:S2 架构全景 · 第 15 篇 | 难度:中级 | 阅读时间:18 分钟
TL;DR
- 架构决策记录(Architecture Decision Record, ADR)是一种轻量级文档方法,用于记录"为什么做出某个技术决策"。coomia-dip 使用 ADR 系统化地追踪从通信协议选择(gRPC vs REST)到构建工具选择(Gradle vs Maven)的每一个关键决策。
- 好的 ADR 不只是记录"选了什么",更重要的是记录"为什么不选其他方案"。被否决的备选方案及其理由,对于未来的开发者理解架构上下文至关重要——它们回答了"当时为什么不用 X"这个最常见的问题。
- ADR 是"活文档"而非"历史归档"。当技术环境变化导致原有决策需要修改时,不是删除旧 ADR,而是创建新 ADR 引用并取代旧 ADR。这样保持了完整的决策演进链路。
#1. 引言:被遗忘的"为什么"
#1.1 每个项目都有的问题
在软件项目的生命周期中,最常被问到的问题不是"这是怎么实现的"(代码回答了这个问题),而是"为什么选择这样实现"。
"为什么内部通信用 gRPC 而不是 REST?"
"为什么 Java 项目用 Gradle 而不是 Maven?"
"为什么 Reasoning & Decision Layer 用 Python 而不是继续用 Java?"
"为什么用 Kafka 而不是 RabbitMQ?"
"为什么用 Doris 而不是 ClickHouse?"
这些问题在 coomia-dip 项目中每周都会被新加入的开发者提出。如果没有系统化的记录,回答通常依赖于"找到当时参与决策的人"——而当那个人离开团队后,这些宝贵的架构上下文就永远丢失了。
#1.2 ADR 是什么
ADR(Architecture Decision Record)是一种轻量级的结构化文档格式,用于记录重要的架构决策。每条 ADR 包含:
- 背景:做决策时面临的情况和约束
- 决策:最终选择了什么
- 备选方案:考虑过但未选择的方案及理由
- 后果:这个决策带来的正面和负面影响
ADR 不是设计文档、不是技术规格书、不是会议纪要——它是一种精炼的决策记录,通常 1-2 页即可完成。
#1.3 coomia-dip 的 ADR 实践
coomia-dip 项目将 ADR 作为架构治理的核心工具之一。在项目的 CLAUDE.md 中,有多条技术红线实际上就是关键 ADR 的执行结果:
❌ 内部服务之间使用 REST/JSON(必须用 gRPC) → ADR-001
❌ Reasoning & Decision Layer + Agent Runtime Layer 使用 Spring Boot(必须用 Python) → ADR-003
❌ Java 项目使用 Maven(必须用 Gradle) → ADR-005
❌ 绕过 Ontology 直接查库 → ADR-007
每一条红线背后都有一个完整的 ADR,解释了决策的背景、理由和权衡。
#2. ADR 模板与格式
#2.1 coomia-dip ADR 模板
coomia-dip 使用以下标准化模板:
# ADR-{编号}: {决策标题}
## 状态
{提议 | 已接受 | 已废弃 | 已取代 by ADR-XXX}
## 日期
YYYY-MM-DD
## 背景
{描述做决策时的情况、约束和需求}
## 决策
{明确声明做出的决策}
## 备选方案
### 方案 A: {名称}
- 优点: ...
- 缺点: ...
- 未选择原因: ...
### 方案 B: {名称}
- 优点: ...
- 缺点: ...
- 未选择原因: ...
## 后果
### 正面
- ...
### 负面
- ...
### 风险与缓解
- 风险: ...
缓解: ...
## 参考
- {相关链接、文档、讨论}
#2.2 ADR 编号规则
coomia-dip 的 ADR 按领域分组编号:
| 范围 | 领域 | 示例 |
|---|---|---|
| 001-099 | 通信与协议 | ADR-001 gRPC vs REST |
| 100-199 | 数据与存储 | ADR-101 Doris vs ClickHouse |
| 200-299 | 计算与运行时 | ADR-201 Python for Reasoning & Decision Layer |
| 300-399 | 构建与工程 | ADR-301 Gradle vs Maven |
| 400-499 | 测试与质量 | ADR-401 Testcontainers |
| 500-599 | 部署与运维 | ADR-501 Docker Compose |
| 600-699 | 安全与治理 | ADR-601 Ontology 强制路由 |
| 700-799 | SDK 与开发体验 | ADR-701 Python SDK 优先 |
#3. 核心 ADR 详解:通信协议
#3.1 ADR-001: gRPC 优于 REST 用于内部通信
状态: 已接受
日期: 2025-06-15
背景:
coomia-dip 由 8 个 Layer 组成,每个 Layer 是独立的服务或服务组。这些 Layer 之间需要高频、低延迟的通信。平台同时使用 Java(Control Layer + Data Layer)和 Python(Reasoning & Decision Layer + Agent Runtime Layer),因此通信协议必须具备跨语言能力。
需要考虑的约束:
- Java 和 Python 之间的序列化兼容性
- 通信延迟对派生属性计算链的影响(见 S2-07)
- 接口定义的版本管理和向后兼容
- 团队的学习成本和工具链成熟度
决策:
所有内部服务间通信必须使用 gRPC + Protobuf。REST/JSON 仅用于面向外部用户的 API 网关层。
备选方案:
方案 A: REST + JSON
- 优点: 团队熟悉度高,调试工具丰富(Postman, curl),浏览器直接可测
- 缺点: JSON 序列化/反序列化开销大(相比 Protobuf 慢 4-5 倍);无强类型接口定义,跨语言兼容性依赖文档而非代码;HTTP/1.1 每个请求一个连接,不支持双向流
- 未选择原因: 在高频内部调用场景下(如派生属性级联计算可能触发数十次跨 Layer 调用),JSON 的序列化开销会从可忽略变为显著瓶颈
方案 B: REST + Protobuf
- 优点: 保留 REST 语义,获得 Protobuf 序列化优势
- 缺点: 非标准组合,工具链支持有限;仍然受限于 HTTP/1.1 的连接模型;代码生成工具不如 gRPC 生态完善
- 未选择原因: 属于"两个世界的折衷",既没有 REST 的生态优势,也没有 gRPC 的完整能力
方案 C: Apache Thrift
- 优点: 与 gRPC 类似的 IDL + 代码生成,性能优秀
- 缺点: 社区活跃度不如 gRPC;流式传输能力较弱;云原生生态集成(Envoy, Istio)不如 gRPC
- 未选择原因: gRPC 已成为云原生通信的事实标准,选择 Thrift 会增加与未来基础设施集成的成本
后果:
正面:
- 跨语言接口兼容性由 Protobuf 编译器保证,消除了"文档与代码不一致"的问题
- 序列化性能提升 4-5 倍,降低了派生属性级联计算的通信开销
- 双向流支持为实时事件推送提供了原生能力
- 与 Kubernetes 生态(gRPC 健康检查、负载均衡)天然集成
负面:
- 调试难度增加——gRPC 使用二进制协议,无法像 JSON 一样直接用浏览器查看
- 团队学习曲线——需要学习 Protobuf 语法和 gRPC 概念
- 工具链要求——需要配置 protoc 编译器和各语言的代码生成插件
风险与缓解:
- 风险: 调试困难影响开发效率 缓解: 引入 grpcurl 和 gRPC 反射 API,提供类似 curl 的调试体验;为开发环境配置 gRPC-Web 代理
- 风险: Proto 文件变更导致跨语言不兼容 缓解: CI 流水线强制双语言编译和契约测试(见 S2-14)
#4. 核心 ADR 详解:技术栈选型
#4.1 ADR-201: Python 用于 Reasoning & Decision Layer + Agent Runtime Layer(智能决策层)
状态: 已接受
日期: 2025-07-02
背景:
Reasoning & Decision Layer(推理与决策)和 Agent Runtime Layer(Agent 运行时)需要处理以下任务:
- 规则引擎评估
- 机器学习模型推理
- AI Agent 编排
- 与 LLM API 集成
团队需要决定这两个 Layer 使用 Java(与 Control Layer + Data Layer 一致)还是 Python。
决策:
Reasoning & Decision Layer 和 Agent Runtime Layer 必须使用 Python 3.x + FastAPI,禁止使用 Spring Boot。
备选方案:
方案 A: Java + Spring Boot
- 优点: 与 Control Layer + Data Layer 技术栈统一,团队 Java 经验丰富,JVM 运行时性能优秀
- 缺点: ML/AI 生态远不如 Python;与 PyTorch、Transformers、LangChain 集成需要通过 JNI 或子进程调用,增加复杂度;AI 领域几乎所有新库和模型都优先提供 Python SDK
- 未选择原因: AI/ML 是 Reasoning & Decision Layer + Agent Runtime Layer 的核心能力,选择 Java 意味着永远在"追赶"Python 生态,而不是"利用"它
方案 B: Python + Django
- 优点: Django 是 Python 最成熟的 Web 框架,ORM 功能强大
- 缺点: Django 是同步框架,异步支持(async views)是后加的,不够原生;Django 的 ORM 在 coomia-dip 中用不上(数据访问通过 Ontology 层);框架太重,很多功能用不到
- 未选择原因: FastAPI 从设计之初就是异步的,完美匹配 AI/ML 工作负载的 I/O 密集特性;Pydantic 集成提供了原生的数据验证和序列化
方案 C: Python + Flask
- 优点: 轻量灵活,学习曲线平缓
- 缺点: 无内置异步支持(需要 Quart 或 ASGI 适配器);无内置数据验证;无自动 OpenAPI 文档生成
- 未选择原因: FastAPI 在异步性能、类型安全和 API 文档方面全面优于 Flask
后果:
正面:
- 直接访问 Python AI/ML 生态(PyTorch, Transformers, LangChain 等)
- FastAPI 的异步原生支持完美匹配 LLM API 调用的高延迟特性
- Pydantic v2 提供了运行时类型验证,与 Protobuf 消息格式互补
- 招聘 AI/ML 工程师更容易——Python 是该领域的通用语言
负面:
- 团队需要同时维护 Java 和 Python 两套技术栈
- Python 运行时性能不如 JVM(对 CPU 密集任务有影响)
- 依赖管理(pip/poetry)不如 Gradle 统一
- 两种语言的代码风格和最佳实践不同,需要双份规范
#4.2 ADR-301: Gradle 优于 Maven 用于 Java 构建
状态: 已接受
日期: 2025-06-20
背景:
coomia-dip 的 Java 项目(Control Layer 和 Data Layer)需要选择构建工具。项目 CLAUDE.md 已将此列为技术红线:
❌ Java 项目使用 Maven(必须用 Gradle)
需要理解这条红线背后的技术考量。
决策:
所有 Java 项目必须使用 Gradle 8.x 构建。
备选方案:
方案 A: Maven
- 优点: 行业标准,约定优于配置,大多数 Java 开发者熟悉,IDE 支持完善
- 缺点: XML 配置冗长,自定义任务需要编写插件;增量构建能力弱——每次构建几乎都是全量;多模块项目构建速度慢(比 Gradle 慢 2-3 倍);不灵活,复杂构建需求需要大量插件配置
- 未选择原因: coomia-dip 的 Control Layer 和 Data Layer 都是多模块项目(10+ 子模块),Maven 的全量构建模式在日常开发中浪费大量时间
方案 B: Bazel
- 优点: Google 开源,极致的增量构建和缓存,支持多语言(Java + Python 统一构建)
- 缺点: 学习曲线极陡,配置复杂度远超 Gradle;Java 生态集成不如 Gradle(很多库没有 Bazel 构建规则);团队无 Bazel 经验
- 未选择原因: Bazel 的优势在超大规模单体仓库中才能体现,coomia-dip 的项目规模使用 Gradle 已经足够
后果:
正面:
- 增量构建使日常开发的构建时间从分钟级降到秒级
- Kotlin DSL 的
build.gradle.kts提供了类型安全的构建配置 - Gradle 对 Protobuf 代码生成的插件支持完善(
protobuf-gradle-plugin) - 与 Spring Boot 和 Quarkus 的 Gradle 插件集成良好
负面:
- 部分团队成员需要从 Maven 过渡到 Gradle
- Gradle 的灵活性有时导致"一千种方式做同一件事"
- Gradle Wrapper 版本管理需要额外关注
#5. 核心 ADR 详解:数据存储
#5.1 ADR-101: Apache Doris 作为 OLAP 引擎
状态: 已接受
日期: 2025-07-10
背景:
coomia-dip 需要一个 OLAP 引擎来支持:
- 大规模数据分析查询
- 物化视图(ComputationCoordinator 的 MATERIALIZED 模式)
- 实时数据写入和近实时查询
- SQL 兼容性(降低学习成本)
决策:
使用 Apache Doris 作为平台的主 OLAP 引擎。
备选方案:
方案 A: ClickHouse
- 优点: 单表查询性能极佳,列式存储压缩率高,社区活跃
- 缺点: 多表 JOIN 性能较差(coomia-dip 的关系查询场景频繁);物化视图功能有限;分布式事务支持弱;实时更新(UPSERT)需要 ReplacingMergeTree 引擎,存在数据短暂不一致窗口
- 未选择原因: coomia-dip 的 Ontology 查询经常涉及对象关系的多表 JOIN,这是 ClickHouse 的短板
方案 B: Apache Druid
- 优点: 实时摄入能力强,时序数据查询优秀
- 缺点: SQL 支持不完整;不支持标准 UPDATE/DELETE;运维复杂度高(多种节点类型);物化视图能力弱
- 未选择原因: coomia-dip 不是纯粹的时序分析场景,需要完整的 SQL 语义
方案 C: StarRocks
- 优点: 与 Doris 架构类似,性能测试略优,物化视图功能强
- 缺点: 商业公司主导,社区版功能受限;国内用户社区不如 Doris 活跃;长期发展方向受商业策略影响
- 未选择原因: Doris 作为 Apache 顶级项目,社区治理更开放,长期可持续性更有保障
后果:
正面:
- MySQL 兼容的 SQL 语法降低了团队学习成本
- 物化视图功能直接支持 ComputationCoordinator 的 MATERIALIZED 模式
- 实时写入 + 近实时查询满足大部分派生属性计算需求
- Apache 基金会项目保证了开源可持续性
负面:
- Doris 的生态(连接器、工具)不如 ClickHouse 丰富
- 极端性能场景下不如 ClickHouse(但 coomia-dip 的查询模式不属于极端场景)
- 需要关注 Doris 版本升级的兼容性
#6. 核心 ADR 详解:架构模式
#6.1 ADR-601: 强制 Ontology 路由
状态: 已接受
日期: 2025-06-25
背景:
项目 CLAUDE.md 的技术红线:
❌ 绕过 Ontology 直接查库
这条红线要求所有数据访问必须通过 OntologyRuntimeService,即使这意味着额外的 gRPC 调用开销(2-5ms)。需要记录这个决策的完整上下文。
决策:
所有服务的数据读写必须通过 OntologyRuntimeService 的 gRPC 接口完成,禁止任何服务直连底层数据库。
理由(详见 S2-04 Ontology 内核专题):
- 统一权限检查:Ontology 层在每次操作中执行权限验证,直连数据库会绕过权限体系
- Schema 验证:Ontology 层保证所有写入符合 ObjectType 定义的约束
- 审计追踪:每次操作自动记录审计日志,直连数据库会产生"不可追踪"的数据变更
- 事件发布:数据变更自动触发 CDC 事件,直连数据库会导致派生属性无法级联更新
被否决的替代方案:
"允许只读直连"——有人提议允许只读场景直连数据库以提高查询性能。被否决的原因:
- 只读查询仍然需要权限检查
- 只读查询也需要审计记录
- "只读直连"一旦开口子,很快会演变为"写入也直连"
- 2-5ms 的 Ontology 开销对大多数场景可接受
#7. ADR 生命周期管理
#7.1 ADR 状态流转
提议 (Proposed)
│
├── 团队讨论 & 技术评审
│
├── 接受 (Accepted)
│ │
│ ├── 环境变化 / 新需求
│ │
│ └── 取代 (Superseded by ADR-XXX)
│ │
│ └── 新 ADR 引用旧 ADR
│
└── 拒绝 (Rejected)
│
└── 记录拒绝原因(同样有价值)
#7.2 何时创建 ADR
不是每个技术决定都需要 ADR。coomia-dip 的判断标准:
| 需要 ADR | 不需要 ADR |
|---|---|
| 选择数据库或消息队列 | 选择日志格式 |
| 选择通信协议 | 选择代码缩进风格 |
| 选择编程语言 | 选择变量命名约定 |
| 确定安全架构模式 | 选择具体的加密算法 |
| 改变系统边界 | 重构内部实现细节 |
简单规则:如果这个决策在 6 个月后有人会问"为什么",就写一个 ADR。
#7.3 ADR 更新策略
ADR 一旦被接受,正文内容不应修改(它记录的是"当时"的决策上下文)。如果需要变更,创建新 ADR:
# ADR-102: 从 Doris 迁移到 Doris + Iceberg 混合架构
## 状态
已接受(取代 ADR-101 中的"Doris 作为唯一 OLAP 引擎"部分)
## 背景
ADR-101 选择 Doris 作为唯一 OLAP 引擎。经过 6 个月的运行,
发现冷数据存储在 Doris 中成本过高。需要引入 Iceberg 作为
冷数据存储层...
#8. ADR 与项目红线的关系
#8.1 红线 = ADR 的强制执行
coomia-dip CLAUDE.md 中的每条技术红线都对应一个或多个 ADR:
| 技术红线 | 对应 ADR | 核心理由 |
|---|---|---|
| 禁止 REST 内部通信 | ADR-001 | 性能 + 跨语言类型安全 |
| Reasoning & Decision Layer + Agent Runtime Layer 禁止 Spring Boot | ADR-201 | AI/ML 生态兼容 |
| 禁止 Maven | ADR-301 | 增量构建效率 |
| 禁止绕过 Ontology | ADR-601 | 权限 + 审计 + 事件 |
禁止 as any | ADR-702 | 类型安全 |
| 禁止空 catch 块 | ADR-401 | 可调试性 |
#8.2 红线的例外流程
如果开发者确实遇到需要突破红线的特殊场景,必须通过以下流程:
1. 提交"红线例外申请 ADR"
├─ 说明为什么必须突破红线
├─ 说明影响范围
└─ 提出缓解措施
2. 架构评审
├─ 至少 2 名 Senior 评审
└─ 记录评审结论
3. 如批准
├─ 在 ADR 中记录"限定范围"的例外
├─ 设置检查点(例外是否仍然必要)
└─ 通知相关 Layer 负责人
#9. ADR 的团队协作价值
#9.1 新成员上手
对于新加入 coomia-dip 的开发者,ADR 是最有效的上手资料之一。通过阅读核心 ADR,新成员可以在 2-3 小时内理解:
- 为什么使用这些技术而不是其他技术
- 历史上考虑过哪些备选方案
- 每个关键决策的权衡和妥协
- 哪些约束是可以挑战的,哪些是不可动摇的
#9.2 跨团队对齐
在 coomia-dip 的多 Layer 架构中,不同团队负责不同的 Layer。ADR 确保所有团队在关键技术决策上保持一致:
Control Layer 团队: "我们想用 REST 暴露一个新接口"
→ 查看 ADR-001 → 理解为什么必须用 gRPC
→ 如果有特殊需求 → 提交例外 ADR
Reasoning & Decision Layer 团队: "我们想引入 Go 重写性能关键路径"
→ 查看 ADR-201 → 理解为什么选择 Python
→ 如果性能需求合理 → 提交新 ADR 评估 Go 引入的可行性
#9.3 技术债务管理
ADR 中记录的"负面后果"和"风险"是技术债务的早期预警系统。团队可以定期回顾 ADR 的预测:
ADR-001 的预测风险: "调试困难影响开发效率"
→ 6 个月后回顾 → 确实出现了调试痛点
→ 已通过引入 grpcurl + gRPC 反射缓解
→ 记录在 ADR-001 的"回顾"部分
#10. ADR 反模式
#10.1 常见反模式
| 反模式 | 描述 | 后果 |
|---|---|---|
| 事后补写 | 决策已执行数月后才写 ADR | 上下文丢失,备选方案回忆不全 |
| 过于详细 | ADR 写成 20 页设计文档 | 没人愿意读和写 |
| 缺少备选方案 | 只记录"选了什么"不说"还考虑了什么" | 未来开发者无法理解决策上下文 |
| 决策不可执行 | "我们应该考虑性能" | 不是具体的、可执行的决策 |
| 永不回顾 | ADR 写完就束之高阁 | 决策可能已过时但无人知道 |
| 偷偷修改 | 直接修改已接受 ADR 的正文 | 破坏决策历史链路 |
#10.2 好的 ADR 的标准
一个高质量的 ADR 应该能让 6 个月后的读者在 5 分钟内理解:
- 当时面临什么问题(背景)
- 做了什么决定(决策——一句话概括)
- 为什么不选其他方案(备选方案——最有价值的部分)
- 需要承受什么代价(后果——诚实面对负面影响)
#11. ADR 工具链
#11.1 存储与组织
coomia-dip 将 ADR 作为代码仓库的一部分:
docs/
└── adr/
├── README.md ← ADR 索引
├── templates/
│ └── adr-template.md ← 标准模板
├── 001-grpc-over-rest.md
├── 101-doris-as-olap.md
├── 201-python-for-intelligence.md
├── 301-gradle-over-maven.md
├── 401-testing-pyramid.md
├── 501-docker-compose-deployment.md
├── 601-ontology-routing.md
└── 701-python-sdk-first.md
#11.2 ADR 与 Git 的集成
ADR 变更遵循与代码相同的版本控制流程:
- ADR 的创建和修改通过 PR 提交
- PR 需要至少 2 名 Reviewer 审批
- 合并后通过 Commit Message 记录(使用
增加:或更新:前缀)
增加: ADR-102 Doris + Iceberg 混合存储架构决策
#11.3 自动化索引
CI 流水线中包含一个自动化脚本,在每次 ADR 变更时重新生成 README.md 索引:
# scripts/generate_adr_index.py
def generate_index(adr_dir: str) -> str:
"""扫描 ADR 目录,生成 Markdown 索引"""
adrs = []
for path in sorted(Path(adr_dir).glob("*.md")):
if path.name in ("README.md", "adr-template.md"):
continue
title = extract_title(path)
status = extract_status(path)
date = extract_date(path)
adrs.append(f"| [{path.stem}]({path.name}) | {title} | {status} | {date} |")
header = "| ADR | 标题 | 状态 | 日期 |\n|-----|------|------|------|\n"
return header + "\n".join(adrs)
#12. ADR 与架构演进
#12.1 决策链路追踪
ADR 之间通过引用关系形成"决策链路":
ADR-001 (gRPC 内部通信)
│
├── ADR-002 (Protobuf Schema 管理)
│ └── ADR-003 (Proto 文件单仓管理)
│
└── ADR-004 (gRPC 流式传输用于事件推送)
└── ADR-005 (Kafka 作为事件骨干 → 替代 gRPC 流)
ADR-101 (Doris 作为 OLAP)
│
└── ADR-102 (Doris + Iceberg 混合架构 → 取代 ADR-101 的部分决策)
#12.2 架构演进的可追溯性
通过 ADR 链路,可以清楚地看到架构是如何一步步演进的:
2025-06: ADR-001 → 选择 gRPC 内部通信
2025-07: ADR-004 → 考虑用 gRPC 流做事件推送
2025-08: ADR-005 → 发现 gRPC 流不适合大规模事件,改用 Kafka
2025-09: ADR-006 → 基于 Kafka 建设 7 种事件角色(见 S2-06)
这个演进链路回答了"为什么现在用 Kafka 做事件推送而不是 gRPC 流"——因为 ADR-004 尝试过,ADR-005 记录了为什么放弃。
#13. 与 Palantir Foundry 的对比
| 维度 | Palantir Foundry | coomia-dip |
|---|---|---|
| 决策记录 | 内部技术备忘录 (闭源) | ADR 公开文档 (代码仓库) |
| 决策追踪 | Confluence + JIRA | Git 版本控制 + Markdown |
| 技术红线 | 内部规范文档 | CLAUDE.md + ADR |
| 新人上手 | 导师制 + 内部培训 | ADR 自助阅读 + 导师制 |
| 决策更新 | 文档更新通知 | PR 审批 + Git 历史 |
| 架构治理 | 架构评审委员会 | ADR 提交 + 同行评审 |
coomia-dip 作为开源项目,ADR 的透明性是一个核心优势。任何贡献者都可以阅读完整的决策历史,理解"为什么",而不只是看到"是什么"。
#14. 实践建议
#14.1 从第一天开始写 ADR
不要等到项目"稳定"了才开始写 ADR。最有价值的决策通常发生在项目早期,而早期的上下文最容易被遗忘。
#14.2 保持轻量
一个 ADR 不应超过 2 页。如果发现写得太长,可能是把设计文档和决策记录混在一起了。ADR 只记录"决策",设计细节放在其他文档中。
#14.3 备选方案是最有价值的部分
花更多时间写"为什么不选 X"而不是"为什么选 Y"。因为"选了 Y"是代码中可见的事实,但"为什么不选 X"是只存在于决策者脑中的隐性知识。
#14.4 定期回顾
每个季度花 1-2 小时回顾现有 ADR:
- 哪些决策的负面后果已经显现?
- 哪些备选方案现在变得更有吸引力了?
- 哪些 ADR 需要创建后续 ADR 来修正?
#Key Takeaways
-
ADR 记录的核心不是"选了什么"而是"为什么不选其他"。 代码本身就是"选了什么"的最佳文档,但"为什么"只存在于决策者的脑中。当决策者离开团队后,这些关键上下文就永远丢失了。coomia-dip 通过 ADR 系统将隐性知识转化为显性文档,确保每个技术红线都有可追溯的决策依据。
-
ADR 是"活文档"而非"历史归档"——旧决策不删除,而是被新决策取代。 这种链式结构保持了完整的决策演进历史。当有人问"为什么从 gRPC 流改成 Kafka 做事件推送",ADR 链路清楚地记录了尝试 → 问题 → 变更的完整过程。
-
技术红线必须有 ADR 支撑,否则就是"独裁"而非"治理"。 coomia-dip 的
CLAUDE.md中每条技术红线都对应一个 ADR,开发者可以阅读 ADR 理解"为什么有这条红线"。如果有合理理由需要突破红线,可以提交例外 ADR 经团队评审。这种机制平衡了架构一致性和灵活性。
“系列回顾: S2 架构全景系列到此完结。从第 1 篇的八 Layer 架构到第 15 篇的架构决策记录,我们完整地呈现了 coomia-dip 的架构设计思路、技术选型理由和工程实践方法。希望这些文章能帮助读者不仅理解"coomia-dip 是什么",更理解"coomia-dip 为什么是这样"。
Tags: #adr #architecture-decision-record #technical-governance #red-line #grpc #gradle #python #doris #ontology #decision-tracking #coomia-dip #智策平台