Hotdry.

Article

ADR 版本化追踪与架构漂移检测:从文档到代码的闭环治理

基于 Martin Fowler 架构指南,构建 ADR 版本化追踪流水线,结合静态分析实现代码与文档的架构漂移自动检测。

2026-06-14systems

Martin Fowler 将软件架构定义为 "专家开发者对系统设计的共享理解",并指出架构漂移(cruft)会在数周内显著拖慢交付速度。架构决策记录(ADR)作为承载这种共享理解的核心载体,若不能与代码演进保持同步,便会沦为无人问津的文档废墟。本文基于 Fowler 的架构治理思想,设计一套可落地的 ADR 版本化追踪流水线,结合静态分析工具实现架构漂移的自动化检测。

架构漂移的成因与危害

在敏捷开发环境中,架构决策往往随着业务需求和技术演进不断调整。当代码实现与文档化的架构决策产生偏差时,便形成了架构漂移。Fowler 强调,高内质质量(internal quality)的架构能在数周内体现价值,而架构漂移的累积效应恰恰相反 —— 开发者需要花费额外时间理解 "实际做了什么" 而非 "应该做什么"。

ADR 的传统管理模式依赖人工维护,存在三个致命缺陷:一是决策状态与代码变更脱节,ADR 中的 "Accepted" 状态无法反映代码层面的真实执行情况;二是决策链路断裂,当新的 ADR 取代旧决策时,代码中可能仍存在遗留实现;三是缺乏量化反馈,架构师难以感知漂移的累积速度。

ADR 版本化追踪流水线设计

版本化存储结构

采用 Git 原生机制管理 ADR 的版本演进,推荐目录结构如下:

docs/
  adr/
    0001-record-architecture-decisions.md
    0002-use-kafka-for-event-streaming.md
    0003-adopt-hexagonal-architecture.md
    superseded/
      0002-use-rabbitmq-for-messaging.md

ADR 文件名采用单调递增编号(NNNN-short-title.md),确保排序一致性。被取代的 ADR 移至 superseded/ 目录,但保留原始文件以便追溯决策历史。

元数据标准化

每份 ADR 遵循标准化模板,包含六个核心字段:

  • Title:简短名词短语,如 "使用 Kafka 处理事件流"
  • Statusproposed | accepted | deprecated | superseded
  • Date:决策创建日期
  • Context:决策背景与约束条件
  • Decision:明确的选择陈述
  • Consequences:正面、负面及中性影响分析

状态流转通过 Git 提交触发:当 PR 合并实现某项决策时,对应 ADR 的状态从 proposed 更新为 accepted,并在 Git 提交消息中引用 ADR 编号(如 implements ADR-0002)。

依赖关系图谱

复杂决策之间存在关联关系,使用 adr link 命令建立显式引用:

# ADR-0004 修正 ADR-0002
adr link 4 Amends 2 "修正了消息队列选型"

通过 adr generate graph 生成 DOT 格式的决策依赖图,可视化决策演进路径。该图谱应作为 CI 产物随版本发布,供团队审查架构决策的完整上下文。

静态分析与架构漂移检测

边界违规检测

架构决策通常定义模块边界(如 "订单服务禁止直接访问用户数据库")。使用依赖分析工具扫描代码,检测违规引用:

# arch-lint.yml
rules:
  - name: order-service-boundary
    source: "services/order/**"
    forbidden:
      - "repositories/user/**"
    message: "违反 ADR-0003:订单服务应通过 API 访问用户数据"

工具在 CI 阶段执行,若检测到违规导入立即阻断构建,并输出关联的 ADR 链接。

技术栈一致性校验

ADR 中记录的技术选型(如 "使用 PostgreSQL 作为主数据库")需要与依赖配置文件(package.jsonpom.xmlgo.mod)保持一致。通过解析 ADR 中的技术关键词,与依赖清单进行交叉验证:

def check_tech_stack_consistency(adr_file, deps_file):
    adr_techs = extract_technologies(adr_file)
    actual_deps = parse_dependencies(deps_file)
    
    drift = []
    for tech in adr_techs:
        if tech.required and tech.name not in actual_deps:
            drift.append(f"ADR 要求使用 {tech.name},但未在依赖中声明")
    
    return drift

架构 fitness function

借鉴演进式架构中的 fitness function 概念,将 ADR 约束转化为可执行的测试用例:

// test/architecture/layer-constraints.test.js
describe('ADR-0005: 分层架构约束', () => {
  it('domain 层不应依赖 infrastructure 层', async () => {
    const violations = await arch()
      .matchFiles('src/domain/**/*.ts')
      .shouldNot()
      .dependOn()
      .matchFiles('src/infrastructure/**/*.ts')
      .check();
    
    expect(violations).toHaveLength(0);
  });
});

这些测试随单元测试一并执行,形成架构治理的自动化防线。

CI/CD 集成方案

流水线阶段设计

将 ADR 校验嵌入标准 CI/CD 流水线:

stages:
  - adr-validate    # 校验 ADR 格式与引用完整性
  - lint            # 代码静态分析
  - arch-check      # 架构约束检测
  - test            # 单元与集成测试
  - drift-report    # 生成漂移报告

PR 门禁策略

在 Pull Request 阶段实施以下检查:

  1. ADR 完整性检查:若 PR 涉及架构变更,必须包含对应的 ADR 新建或更新
  2. 状态一致性检查:代码实现与 ADR 状态必须匹配(accepted 状态的 ADR 必须有对应代码实现)
  3. 依赖关系检查:验证 ADR 之间的引用链接有效
#!/bin/bash
# .github/workflows/adr-check.sh
if git diff --name-only HEAD~1 | grep -E "^(src|services)/"; then
  if ! git diff --name-only HEAD~1 | grep "^docs/adr/"; then
    echo "错误:代码变更涉及架构调整,但未更新 ADR"
    exit 1
  fi
fi

漂移报告生成

定期(建议每周)生成架构漂移报告,包含以下指标:

  • 决策覆盖率:已文档化的架构决策数量 vs. 代码中隐含的架构假设
  • 漂移指数:违反 ADR 约束的代码位置数量
  • 决策半衰期:从 acceptedsuperseded 的平均时间

报告以 Markdown 形式提交至 docs/adr/reports/,并触发团队评审会议。

可落地参数与监控清单

工具链配置

工具类型 推荐工具 配置要点
ADR 管理 adr-tools 统一模板路径:docs/adr/templates/
依赖分析 depcheck/arch-lint 配置边界规则文件:arch-rules.yml
静态扫描 SonarQube/ESLint 启用架构规则插件,阈值设为阻断级
报告生成 adr-tools + Graphviz 输出目录:docs/adr/reports/

关键阈值设定

  • 阻断阈值:单个 PR 引入的架构违规数 > 0 时阻断合并
  • 告警阈值:模块间耦合度较 ADR 基线增长 > 15% 时触发告警
  • 审查阈值:超过 30 天未更新的 proposed 状态 ADR 自动标记为过期

团队实践清单

  • 每个涉及架构变更的 PR 必须关联 ADR 更新
  • ADR 状态流转必须通过 Git 提交完成,禁止手动修改
  • 每周架构站会审查漂移报告,决策修复优先级
  • 新成员 onboarding 包含 ADR 图谱阅读环节

结语

ADR 版本化追踪与架构漂移检测的本质,是将 Fowler 所说的 "共享理解" 转化为可验证的工程实践。通过 Git 原生机制管理决策演进,结合静态分析建立代码与文档的反馈回路,团队能够在架构漂移的萌芽阶段即发现并修正偏差。这套流水线的核心价值不在于工具本身,而在于建立 "决策即代码" 的治理文化 —— 架构不再是文档中的静态蓝图,而是随代码持续演进的活文档。

参考资料

  • Martin Fowler, "Software Architecture Guide", martinfowler.com
  • hasCode, "Managing Architecture Decision Records with ADR-Tools"
  • Michael Nygard, "Documenting Architecture Decisions"

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com