架构漂移检测:保持代码与设计的一致性
在你的组织中,某处存在着一张错误的架构图。也许它展示了一个六个月前已被合并到其他服务中的微服务。也许它标注着 Redis 作为缓存层,而团队早在一次生产事故中就切换到了 Memcached。也许它描绘了一个整洁的六边形架构,而这个服务实际上已经积累了足够多的捷径和变通方案,看起来更像一团意大利面。
这就是架构漂移:系统文档化描述与实际运行方式之间逐渐的、无声的偏离。与 Bug 不同,漂移不会触发告警。与性能退化不同,它不会出现在监控中。它静静地潜伏着,直到有人基于过时的文档做出了决策——而那个决策最终被证明是错误的。
架构漂移是普遍存在的。每个团队都会经历它。问题不在于你的文档是否会漂移,而在于你多快能发现它,以及你将如何应对。
什么是架构漂移?
架构漂移是指软件系统的实际实现偏离其文档化或预期架构的现象。这一术语诞生于学术软件工程界,但对于每一位实践中的工程师来说都再熟悉不过了。
漂移在架构文档的各个层面都会出现:
结构漂移
文档化的结构不再与代码库匹配:
- 一个记录为独立容器的服务被吸收进了单体应用
- 一个组件被重命名了,但图表仍然显示旧名称
- 一个新服务被创建了,但从未被添加到架构模型中
- 数据库从 MySQL 迁移到了 PostgreSQL,但容器图仍然标注为 MySQL
行为漂移
文档化的行为不再与现实匹配:
- 一个同步 API 调用被替换为异步消息,但关系描述仍然标注为 "REST/HTTP"
- 数据流被改为通过 API Gateway,但图表仍显示服务间的直接通信
- 添加了一个认证步骤,但未反映在系统上下文图中
依赖漂移
文档化的依赖关系不再匹配实际的集成情况:
- 一个第三方 API 被替换为内部自研方案
- 添加了一个新的外部依赖(支付服务商、监控服务),但未被记录
- 一个集成已被停用,但仍出现在系统上下文图中
决策漂移
文档化的架构决策不再被遵循:
- 一条 ADR 规定"所有持久化存储使用 PostgreSQL",但某个团队开始使用 MongoDB
- 合规性规则规定"前端不得直接访问数据库",但有人添加了客户端的 Supabase 集成
- 部署架构标明"单区域部署",但服务实际上已部署到了多个区域
架构漂移发生的原因
理解漂移的成因对于预防至关重要。漂移通常并非出于恶意甚至疏忽——它是软件开发方式的自然产物。
速度优先于文档
当需要在周五之前交付功能时,更新架构图往往是第一个被舍弃的。代码变更才是可交付成果,文档更新只是额外开销。这在短期内是理性的行为,但从长远来看却是灾难性的。
众多微小变更
漂移很少发生在某个戏剧性的时刻。它通过数百个微小的变更逐渐累积,每一个都小到不值得更新文档:
- 重命名一个文件
- 添加一个工具包
- 更换一个库依赖
- 将一个函数提取到独立模块中
没有哪个单独的变更重要到需要触发文档更新。但它们加在一起,却改变了整个架构。
团队人员流动
当工程师离开时,他们带走了隐性知识。新团队继承了代码库,却没有继承对其结构设计原因的理解。他们基于代码中看到的内容而非文档所述来进行修改,进一步加剧了漂移。
缺乏反馈循环
如果没有人检查文档是否与现实匹配,漂移就是不可见的。没有检测机制的情况下,发现漂移的唯一途径就是在事故中、审计中,或者当新工程师指出图表与代码不一致时。届时漂移可能已经相当严重了。
紧急变更
生产事故常常需要架构上的捷径:绕过 API 层的直接数据库连接、硬编码的配置而非使用配置服务、本应临时的缓存变成了永久方案。这些变更绕过了正常的审查流程,且几乎不会被记录下来。
架构漂移的代价
漂移不仅仅是美观问题。它有着具体的、可衡量的代价。
错误决策
当架构师基于过时的文档做决策时,那些决策可能是错误的。"这个服务流量不大,所以我们可以使用同步依赖"——但文档已经过期了,该服务实际处理的负载是文档记录的 10 倍。
新人上手缓慢
新工程师依赖架构文档来建立心智模型。如果文档是错误的,他们就会建立错误的心智模型。他们编写的代码不符合实际架构。他们提出的问题暴露了他们的困惑,消耗了资深工程师的时间。
事故响应
在生产事故中,架构图应该帮助团队理解影响范围和依赖关系。如果这些图是错误的,团队会浪费宝贵的时间追踪错误的依赖链或遗漏关键的上游系统。
合规和审计失败
在受监管行业中,架构文档通常是合规所需的(SOC 2、ISO 27001、HIPAA)。如果审计人员发现文档与现实不符,这将构成一项发现——可能是严重的发现。
AI Agent 的混淆
随着 AI 编码 Agent 日益普及,它们越来越依赖架构文档来获取上下文。一个读取了过时 C4 模型的 Agent 会生成符合文档化架构而非实际架构的代码。这只会放大漂移,而不是修复它。
如何检测架构漂移
人工审查(传统方法)
最简单的方法是定期人工审查:召集团队,逐一检查架构图,确认它们是否仍然与现实匹配。
适用场景:小团队、简单架构、按季度进行。
失效场景:大型系统、快节奏团队,或者最了解代码的人没有时间参加评审会议。人工审查还容易受到确认偏差的影响——人们倾向于看到他们期望看到的东西。
架构适应度函数
适应度函数由 Neal Ford 和《Building Evolutionary Architectures》一书推广,是验证架构属性的自动化测试:
// 示例:确保 handler 包中没有直接的数据库导入
func TestNoDatabaseImportsInHandlers(t *testing.T) {
packages := analyzeImports("./internal/handler/...")
for _, pkg := range packages {
for _, imp := range pkg.Imports {
assert.NotContains(t, imp, "database/sql",
"Handler %s imports database/sql directly", pkg.Name)
assert.NotContains(t, imp, "gorm.io",
"Handler %s imports GORM directly", pkg.Name)
}
}
}
适应度函数在执行特定规则方面非常强大,但它们需要前期投入来编写和维护。它们检查的是约束条件,而非完整模型。
静态分析工具
ArchUnit (Java)、Deptrac (PHP) 和 go-arch-lint (Go) 等工具可以分析代码结构并强制执行依赖规则:
// go-arch-lint 配置
components:
handler:
in: ./internal/handler/
service:
in: ./internal/service/
repository:
in: ./internal/repository/
rules:
handler:
can_depend_on: [service]
service:
can_depend_on: [repository]
repository:
can_depend_on: []
这些工具非常适合在单个代码库中强制执行分层架构。但它们无法解决跨服务漂移问题,也无法验证架构模型是否与代码匹配。
自动化漂移评分
这正是 Archyl 采用的方法。它不是检查特定规则,而是将整个架构模型与代码库进行验证:
- 每个文档化的系统是否对应一个代码仓库?
- 每个文档化的容器是否对应代码库中的一个目录?
- 每个文档化的代码元素是否引用了一个仍然存在的文件?
- 每个文档化关系的两端是否仍然有效?
结果是一个漂移评分(0-100)和详细的分解报告,精确显示哪里发生了漂移。这是最全面的方法,因为它验证的是完整模型,而不仅仅是特定约束。
Archyl 漂移检测的核心设计决策:
轻量级。 不消耗 AI token,不读取文件内容。仅通过 Git 服务商 API 进行文件路径存在性检查。这意味着漂移评分只需几秒钟,而非几分钟。
确定性。 相同的代码库、相同的模型,得到相同的评分。不会因 LLM 温度参数或 prompt 工程而产生变化。
低成本。 每次推送都可以运行,无需担心成本。一天计算一百次完全没问题。
可操作。 分解报告精确显示哪些元素发生了漂移,让你清楚知道该修复什么。
如何预防架构漂移
检测是必要的,但还不够。目标是从一开始就防止漂移积累。
将文档更新纳入完成定义
如果代码变更修改了架构,那么 PR 应该包含文档更新。在你的 PR 模板中添加一个复选框:
## 检查清单
- [ ] 测试通过
- [ ] 代码已审查
- [ ] 架构文档已更新(如适用)
这不能捕获所有情况,但它建立了文档是一等可交付成果的期望。
在 CI 中自动化漂移检测
最有效的预防机制是一个当漂移超过阈值时会失败的 CI 门控:
on:
push:
branches: [main]
jobs:
drift:
runs-on: ubuntu-latest
steps:
- uses: archyl-com/actions/drift-score@v1
with:
api-key: ${{ secrets.ARCHYL_API_KEY }}
organization-id: ${{ secrets.ARCHYL_ORG_ID }}
project-id: 'your-project-uuid'
threshold: '70'
当构建因漂移评分下降而失败时,就必须有人在合并前修复它。文档准确性变得像通过测试一样不可妥协。
建议从较低的阈值(50-60%)开始,随着团队养成习惯逐步提高。
使用 Architecture-as-Code
当你的架构模型以文本格式定义(Structurizr DSL、Archyl YAML)时,它可以与代码一起进行版本控制。这意味着:
- 架构变更出现在 Pull Request 中
- 变更由团队审查
- 架构演进的历史被记录在 Git 中
这远优于在 GUI 工具中定义的架构,后者的变更是不可见且无法审查的。
设置漂移告警
Archyl 支持漂移事件的 Webhook 告警:
drift.score_computed:每次漂移计算时触发。发布到 Slack 频道以提高可见性。drift.score_degraded:当评分下降 10 分以上时触发。这是你的预警系统。
将这些告警配置到你的团队监控的频道。意识到问题是采取行动的第一步。
进行架构评审
每月或每季度的架构评审有多重目的:
- 验证文档化的架构是否仍然与现实匹配
- 识别自动化工具遗漏的漂移(例如行为漂移)
- 讨论漂移的组件应该在代码中修复还是在文档中更新
- 审查和更新可能需要重新考虑的 ADR
采用合规性规则
合规性规则定义了应该始终为真的架构约束:
- "前端容器不得依赖数据库容器"
- "所有公共 API 必须通过 API Gateway"
- "每个服务必须拥有自己的数据库(不共享数据库)"
在 Archyl 中,合规性规则在平台中定义,并通过合规性检查功能强制执行。AI Agent 可以通过 MCP 读取这些规则,并在生成代码时遵守它们。
合规性规则与漂移检测是互补的。漂移检测检查你的模型是否与现实匹配。合规性检查检验现实是否遵循你的规则。
架构漂移 vs. 架构侵蚀
这两个术语相关但不同:
架构漂移是文档与实现之间的偏离。代码本身可能完全没问题——只是文档是错误的。
架构侵蚀是架构本身的退化。代码违反了架构原则,积累了技术债务,变得越来越难以维护。侵蚀是代码质量问题。漂移是文档准确性问题。
它们常常同时发生。当文档漂移时,团队对预期架构失去了认知。没有这种认知,他们就会做出侵蚀架构的变更。漂移助长了侵蚀。
这就是为什么漂移检测的意义超越了文档准确性本身。准确的文档作为参考,可以防止侵蚀。当每个人都能看到预期的架构时,他们更有可能去维护它。
随时间衡量和跟踪漂移
单次漂移评分有用。但趋势更有力量。
建立基线
运行你的第一次漂移计算以确定当前状态。如果评分较低不必恐慌——大多数未主动维护架构文档的团队会看到 40-70% 的评分。
设定目标
制定切合实际的改进目标:
- 第 1 个月:通过修复最明显的漂移,从基线提升至 60%
- 第 3 个月:通过将文档更新纳入工作流程,达到 75%
- 第 6 个月:通过 CI 门控和定期评审,保持在 80% 以上
跟踪趋势
Archyl 存储每次漂移计算及其完整分解。漂移历史视图展示了评分的时间线,让你可以看到:
- 漂移是随时间好转还是恶化?
- 某个特定的迭代或发布是否导致了显著下降?
- CI 阈值是否有效防止了退化?
庆祝进步
当团队提升了漂移评分时,给予肯定。架构文档是吃力不讨好的工作。让进步可见并得到认可,有助于强化这一行为。
漂移检测在 AI 辅助开发中的角色
AI 编码 Agent 的兴起使漂移检测比以往更加重要。
AI Agent 越来越依赖架构文档来获取上下文。通过 MCP 等协议,Agent 可以在生成代码之前读取你的 C4 模型、ADR 和合规性规则。这使它们更加高效——它们生成的代码符合你的架构,而不是凭猜测。
但这只有在文档准确的情况下才有效。一个读取了过时 C4 模型并基于它生成代码的 Agent,会产生符合错误架构的代码。Agent 放大了漂移而非阻止了它。
漂移检测创建了让 AI Agent 保持诚实的反馈循环:
- Agent 通过 MCP 读取架构
- Agent 生成代码,使其符合文档化的架构
- 代码被合并,可能改变了实际架构
- 漂移检测运行,捕获任何偏离
- CI 门控失败,如果漂移超过阈值
- 团队更新文档以反映现实
- Agent 读取更新后的架构——循环闭合
没有第 4 步,循环就是开放的。文档变得越来越虚构。Agent 越来越多地生成符合虚构架构的代码。差距随每次提交而扩大。
漂移检测是闭合这个循环的机制。
开始使用漂移检测
如果你没有架构文档
从 AI 发现开始。将你的代码仓库连接到 Archyl,运行发现功能,审查生成的 C4 模型。这会给你一个大约 70-80% 准确的基线模型。然后设置漂移检测来维持这个准确度。
如果你有现有文档
在支持漂移检测的工具中导入或重新创建你的架构模型。运行第一次漂移计算。评分会精确告诉你当前文档的准确程度——而分解报告会显示你应该首先修复什么。
如果你已经在跟踪漂移
将漂移检测集成到 CI 中。设置阈值。配置告警。开始跟踪趋势。将漂移作为团队指标,而非一次性审计。
无论你从哪里开始
最重要的是开始行动。架构漂移就像技术债务——它会随时间复利增长。你越晚解决它,追赶起来就需要越多的工作。但与技术债务不同的是,漂移检测可以在几分钟内设置好,并立即提供价值。
你的架构文档要么在反映现实,要么没有。现在你可以衡量它究竟是哪种情况了。
了解更多关于架构文档维护的内容:架构漂移评分的工作原理 | 什么是 C4 模型? | AI 驱动的架构文档。或者 免费试用 Archyl,在几分钟内计算你的第一个漂移评分。