架构决策
将架构问题转化为具体、合理、可验证的决策。
该技能负责决策方法、记录生命周期和交付物。engineering-philosophy 负责共享的设计和写作原则。活跃代理和任务负责权限、委派和输出意图。
1. 确立决策
陈述问题、受影响的系统、所需结果和范围排除项。
当一个问题对跨组件契约、质量要求、所有权、信任、操作、依赖或未来变更成本产生实质性影响时,才有必要为其编写 ADR。
常规的局部设计不需要 ADR。除非调用者明确要求正式记录,否则返回 no-adr-needed 并附上答案和简要理由。
识别相关决策。将可独立逆转的选择拆分为独立的记录,但不要将一项连贯的决策碎片化为每一步实现对应的 ADR。
对于现有的 ADR,确立当前任务是澄清、重新评估还是替换。不要将历史解释静默地转换为新的推荐。
2. 建立上下文和驱动因素
阅读适用的指令、相关已接受决策以及足够的实现证据,以理解受影响的边界。
在描述应该存在的状态之前,先确立当前存在的状态。对于全新的项目,使用提供的需求并标明缺少实现证据。
区分:
- 硬约束:必需的契约、策略、兼容性、数据规则或明确限制。
- 决策驱动因素:区分可行选项的质量和权衡。
- 假设:推荐所依赖的未经验证的条件。
确定哪些驱动因素最重要。仅在可辩护时,推断出一个临时的优先级并标明它。当未解决的优先级将实质性改变产品行为、成本或风险接受度时,请求授权。
使模糊的目标具体化。对于性能或可靠性要求,在已知时识别工作负载、故障场景、可观察结果和阈值。将仅为讨论而发明的阈值标记为“提议的”,绝不能标记为需求。
不要制造流量、服务级别目标、截止日期、团队容量、成本或未来需求来证明设计的合理性。
3. 解决改变决策的未知因素
调查可能改变可行性、排名、安全性或实施范围的不确定性。
优先寻找当前系统的本地证据。对于外部行为,使用主要文档并检查相关版本和环境。
区分直接观察、提供的证据、推断和未经测试的假设。供应商的能力并不能证明本地配置已启用该能力。
当证据足以支持决策,或进一步阅读不太可能改变决策时,停止研究。
当需要度量时,定义一个有限范围的实验:问题、代表性工作负载、比较基线、可观察结果、通过/失败标准,以及结果如何改变选择。
不要声称实验已经运行。当执行或源访问超出活动代理权限时,返回具体的证据请求。
4. 比较可行的选项
考虑保留当前设计以及最小的充分变更。解释何时任一选项未能满足硬约束。
仅比较真正可行的替代方案。不要为了中意方案胜出而编造薄弱候选者,不需要强制规定固定数量的选项,也不要研究某个类别中的每一个产品。
基于相同的决策驱动因素评估所有选项。包括相关的迁移工作量、运维所有权、故障处理、经常性成本、耦合度和可逆性。
明确拒绝违反硬约束的选项。不要用加权分数将它们平均掉。
优先使用具体原因进行定性比较。仅当数值评分的输入和权重是可辩护时,才使用它;并暴露其对不确定输入的敏感性。
一项技术的流行度、一个命名的架构模式或声称的可扩展性,其本身并非特定项目的好处。
5. 挑战优选选项
追溯跨越所提议边界的重要成功路径和最重要的可信故障路径。
仅考虑相关关注点:
- 所有权和契约:权威状态、允许的依赖、兼容性、事务和生命周期所有权。
- 故障和恢复:部分完成、重复交付、排序、重试、超时、取消、过载和修复。
- 安全和隐私:信任边界、授权、隔离、敏感数据和可信的滥用路径。
- 运维和变更:部署、可观测性、资源边界、容量证据、迁移、持续支持和退出成本。
使用架构参考中的设计原则,而不是在此重复其规则。
陈述对推荐方案最强烈的反对意见,以及可能推翻它的证据。检查是否有更简单的选项能满足相同的约束。
对于有风险的转型,描述共存和恢复方案。标识出无法安全或不可能回滚的点。当回滚会导致数据丢失或破坏兼容性时,采用“向前滚动”或修复策略。
仅当差异重要时才描述当前、过渡和目标状态。不要仅仅为了装饰决策而生成图表。
6. 决策
当证据支持时,推荐一个方向。指明决定性的驱动因素以及团队将接受的成本或限制。
当已披露的假设不会使安全推荐无效时,在该假设下推进。不要因不重要的未知因素而受阻。
当某个重要假设仍未解决时,说明暂定的偏好和会改变它的确切条件。不要将其呈现为无条件的承诺。
当没有选项满足硬约束时,如实说明。确定所需的最小约束更改或实验;不要悄然削弱需求。
使用以下一种任务结果:
| 结果 | 含义 |
|---|---|
recommended | 在既定约束和假设内,支持一个具体推荐。常规审批可能仍未完成。 |
needs-evidence | 已命名的证据缺口阻止了可靠的选择。尽可能包含暂定偏好和解决该缺口的检查方法。 |
needs-authority | 必须由授权人员解决材料需求、异常或风险决策。解释技术推荐和所需的具体授权。 |
no-adr-needed | 该问题是常规的局部设计。回答它,无需强加正式的架构流程。 |
这些结果描述的是任务结果,而非 ADR 审批、实施完成或代码审查结论。
7. 记录决策
如果仓库存在 ADR 惯例,则使用它。否则,阅读 ADR 模板 并使其适应决策。
记录必须保留问题、上下文、决定性驱动因素、选定方向、可信替代方案、已接受的缺点、不确定性和验证条件。保持足够简洁以易于维护。
一条记录包含一个连贯的架构决策。链接相关记录,而不是复制其理由。
如果没有存储惯例,建议使用 docs/decisions/。在提出最终标识符之前检查现有编号。建议的路径不是已保存的文件。
生命周期和权限
默认将新的 ADR 状态设置为 proposed。
仅当指定权威提供了接纳证据,或者项目明确将该决策委派给调用者或活跃代理时,才使用 accepted。记录接纳的依据。要求推荐、起草或评估并不是接纳。
选定的技术选项可能仍保持 proposed 状态,等待常规审批。不要仅仅因为审批尚未发生,就使用 needs-authority。
遵循项目的剩余生命周期状态。如果没有惯例,在适当时使用 rejected、deprecated 和 superseded。
保留已被接受的历史理由。一项实质性改变的决策应该有一条新的记录,并表明其取代关系。旧记录仍然可访问;当新决策被采纳时,授权作者更新其状态和替换链接。
与 ADR 相矛盾的实施并不会自动取代它。确定需要纠正的是实施、已记录的决策还是管理要求。
接纳并不代表已实施、运维验证或已获得部署许可。
证据和确认
在与上下文或理由相邻的位置引用有影响力的结论。尽可能使用持久的仓库引用和特定版本的外部来源。
不要在持久的 ADR 中仅使用无法访问的会话标识符作为唯一证据。保留指向底层来源的链接,或清晰地归因于提供的证据。
定义用于确认架构符合性和承诺结果的检查。将每个重要的不变量与一个可观察的检查点连接起来。
仅在已知时使用项目支持的命令。否则,描述测试或检查而不发明可执行指令。
将未来的检查标记为“未运行”。在已知时通过既定角色识别责任人,否则将责任人留空。不要将工作分配给不相关的人。
定义具体的重新审视触发器:验证失败、约束变更、达到的阈值或相关的依赖变化。“需要时重新审视”是没有用的。
可交付成果
遵循调用者明确的格式。否则,以一个简洁的决策简报开头:
- 推荐:选定的方向及决定性原因,或未解决的选择。
- 结果:四种任务结果之一。
- 范围:受影响的边界和重要的排除项。
- 信心:高、中或低,附上证明该标签合理性的证据和假设。不要将其描述为度量后的概率。
对于重要的决策,返回完整的 ADR。当选择仍然受阻时,保持其 proposed 状态并将选择标记为暂定或未解决。
仅在需要时添加简短的交接说明。识别约束性条件、未解决的证据或授权,以及下一个负责的角色。引用 ADR 而非重复其内容。
不要将交接说明变成分阶段的实施计划、代码审查报告或主动提供的补丁。
对于 no-adr-needed,返回答案、结果和理由,不要包含空的模板章节,除非调用者请求了记录。
最终检查
确认结果回答了实际的决策问题,在可支持时标识了优选选项,并解释了为何未选择最有力的替代方案。
检查硬约束、现有决策、运营后果、验证声明、接纳权限和范围边界。
删除编造的事实、装饰性的分数、不必要的层次、重复的理由以及声称未完成的工作已完成的主张。