Architecture Drift Score:你的文档在说实话吗?
没人能审计的指标,就是没人该据以行动的指标。所以这篇文章讲的就是算术本身:Architecture Drift Score 是怎么算出来的,什么进了分母,我们刻意排除了什么,以及这项检查看不到的四件事。
这个分数只回答一个问题。你文档化的架构中,有多少比例仍然存在于代码库里? 它是一个 0 到 100 的数字,由向你的 Git 提供者发起的一次请求计算得出,路径中没有 AI,也不读取任何文件内容。
如果你想了解的是问题本身而不是算术,架构漂移指南讲的是什么是漂移、它为什么发生,以及其他检测方法。先从那里开始,然后再回来。本页假定你已经想要一个数字,并且想知道该不该相信它。
读懂这个数字
在 Archyl 中打开任意项目,点击顶部栏的心跳图标,然后点击 "Compute Drift Score"。几秒钟后你会得到一个数字:
- 90-100% — 优秀。你的文档与代码库高度吻合。
- 70-89% — 良好。大体准确,有一些差距需要弥补。
- 50-69% — 一般。检测到明显漂移。是时候更新了。
- 低于 50% — 你的文档是虚构的。
这些区间是我们对什么值得处理的判断,而不是对任何东西的测量。它们下面的那个数字则是精确的。
这个数字是怎么算出来的
你模型中的每个元素都会被归入一个 bucket,分数就是存活下来的那部分占比:
score = floor( (matched + 0.5 × partial) / total × 100 )
total = matched + partial + missing_in_code + new_in_code
- matched — 模型说它存在,仓库也同意。
- missing_in_code — 有文档记录,却找不到。目录已消失的 Container,文件已被删除的代码元素。
- new_in_code — 在仓库中找到了,但模型里没有。没有文档记录,这是反方向的漂移,对你的扣分力度分毫不差。
partial 只计半分,留给那些能匹配上但存在差异的元素。目前的检查不会产生它:每个元素都会落入其他三类之一,所以实际上分数就是 matched 的占比。我们之所以告诉你,是因为一个含有永远不会触发的项的公式,属于那种你应该从我们这里听到、而不是自己发现的事情。
比较两次运行时有两个细节很重要。结果是截断而不是四舍五入,所以 89.9 会报告为 89。而且未文档化的元素会撑大分母,这就是为什么新增三个服务却不为它们写文档会让你的分数下降——尽管你已经写下的内容没有一条变成了假话。
实际检查了什么
漂移分析在设计上是轻量级的:只需向你的 Git 提供者发起一次递归的树请求,无需 AI,无需获取文件内容。它从五个维度验证你的架构:
Systems — 你的仓库名称是否与文档化的系统匹配?我们使用与 AI 发现管道相同的 PascalCase 命名约定,并通过模糊匹配使 EkoAuthz 能够匹配名为 authz 的仓库。
Containers — 仓库中的顶层目录是否对应文档化的 Container?frontend/ 匹配 FrontendWebApp。backend/ 匹配 BackendApiServer。没有源码目录的基础设施 Container(数据库、队列、监控)会被排除,因为它们是外部服务的有效文档,而不是漂移。下一节会讲这个排除的代价。
Components — 每个 Container 下的组件是否仍然有效?如果父 Container 的目录存在,其组件被视为有效。如果 Container 目录消失了,其所有组件都会被标记。
Code Elements — 这是最精确的检查。C4 model 中的每个代码元素都有一个 filePath。我们验证每个文件是否仍然存在于仓库中。文件被重命名了?类被删除了?模块被移动了?Drift Score 会立即捕获。
Relationships — 如果关系的源元素和目标元素都通过了验证,则该关系有效。如果任一端点发生了漂移,该关系会被标记。
结果是按元素的详细分类,精确显示什么匹配了、什么缺失了、什么是新的——不是一个不透明的分数,而是一份可操作的报告。
分母里排除了什么
一个分数的诚实程度,取决于它拒绝去数的那些东西。三条排除规则,都是刻意为之:
外部系统和人。 任何被标记为外部系统或人的元素,在比较之前就会从两侧被剔除。Stripe、你的身份提供商和"客户"属于 System Context 图,它们谁也不会出现在你的仓库里。把它们算作缺失,等于因为你画了一张正确的图而惩罚你。
没有源码目录的基础设施 Container。 一个匹配不到任何目录的文档化 Container,会被从 Container 的统计中移除,而不是算作漂移。你的 PostgreSQL 实例、你的 Kafka 集群和你的 Datadog 账户都是正当的 Container,而它们没有一个是文件夹。
这条规则有代价,你应该知道:一个你确实删掉的服务目录,同样会被排除在 Container 统计之外,因为这项检查分辨不出"数据库"和"我们上个迭代删掉的服务"。它的组件不会被排除。由于父 Container 没有匹配上,这些组件仍会被判定为缺失——所以被删除的服务确实会体现在分数里,只是比你预期的位置低了一层。
没有记录文件路径的代码元素。 如果模型中的某个代码元素没有 filePath,那就没有可验证的东西,于是它被跳过,而不是被猜测。它既不为你加分,也不为你扣分。生成的路径和 vendor 路径(vendor/、node_modules/、dist/、target/、__pycache__/ 以及其余那些常见目录)在这一切开始之前,就已经从文件树中被过滤掉了。
为什么轻量级很重要
我们有意选择不运行完整的 AI 发现管道来检测漂移。原因如下:
速度。 AI 分析大型仓库需要几分钟。漂移评分只需几秒。你可以在每次 push 时运行,而不会拖慢你的管道。
确定性。 AI 可能因模型温度、提示变化和 token 限制的不同,在同一代码库上产生不同的结果。文件路径的存在是二元的——文件要么在,要么不在。你的分数是可复现的。
成本。 不消耗 AI token。不触及 API 速率限制。想运行一百次就运行一百次。
简单性。 算法是可审计的。检查文件路径,匹配目录名称,验证关系。没有黑盒。
这个分数看不到什么
上面每一条特性,都是用同一笔交易换来的:这项检查读的是结构,不是代码。由此带来四个后果,其中没有一个是我们打算隐瞒的 Bug。
行为层面的漂移是不可见的。 如果两个服务保留了各自的名称和目录,而它们之间的同步 HTTP 调用变成了队列消息,分数不会有任何变化。结构上什么都没变。这是最大的盲区,而且没有廉价的解决办法:要抓住它,就得读代码,或者由人来评审模型。
移动看起来和删除一模一样。 代码元素是按精确、区分大小写的文件路径来验证的。把 internal/auth/token.go 移到 internal/identity/token.go,哪怕一行内容都没改,这个元素也会被报告为缺失。这在技术上是正确的,因为文档里记录的路径确实错了;这也意味着一次重命名目录的重构会让你的分数以一种看起来很吓人的方式下跌,而实际的解决方式是每个元素改一行。
组件层面的准确性是继承来的,不是验证出来的。 只要 Container 的目录存在,它下面的每个组件都会被推定为有效。检查从不往里看。所以一个仍然存在、但已被掏空重写的 Container,在组件层面会被评为干净,而这个数字对你的 Level 3 图的自信程度,超过了证据所能支撑的范围。
名称匹配很宽容。 Systems 和 Containers 按名称进行三轮匹配:先是忽略大小写的精确匹配,然后是双向的子串包含,最后是拆分 PascalCase 和 kebab-case 之后的 token 重叠。EkoAuthz 能匹配到名为 authz 的仓库;BackendApiServer 能匹配到名为 backend 的目录。正是这一点让琐碎的命名差异不会被报成漂移,而它的偏差方向是倾向于给你的模型留有余地。如果你想要严格的读数,请用按元素的明细,而不是头条上的那个数字。
综合来看,这个分数很好地衡量了你的模型是否仍在描述同一个系统,却只能很弱地衡量它描述得是否正确。把高分理解为"没有结构性意外",而不是"文档是对的"。
追踪趋势,而非快照
单个分数是有用的。趋势是强大的。
每次漂移计算都会连同完整分类一起存储。Overview 标签页显示你的分数随时间变化的柱状图。点击任意柱子加载该历史报告,精确查看发生了什么变化。
这将漂移评分从一次性审计转变为持续的健康指标。你可以看到:
- 上周的重构是改善了还是降低了文档准确性?
- 漂移是否随时间恶化,你在工作流上做的改动中有哪些让它慢了下来?
- 哪个迭代引入了最多未记录的变更?
在 CI 中强制执行
不强制执行的指标就是你会忽视的指标。这就是为什么我们构建了一个 GitHub Action。
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'
设置 threshold: '70',当架构文档准确率低于 70% 时,action 将失败。作业摘要会显示包含完整分类的格式化表格——直接在你的 PR 检查中可见。
你还可以将分数作为 PR 评论发布:
- uses: archyl-com/actions/drift-score@v1
id: drift
with:
api-key: ${{ secrets.ARCHYL_API_KEY }}
organization-id: ${{ secrets.ARCHYL_ORG_ID }}
project-id: 'your-project-uuid'
- uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## Architecture Drift: ' +
'${{ steps.drift.outputs.score }}%\n' +
'Matched: ${{ steps.drift.outputs.matched-count }}' +
' / ${{ steps.drift.outputs.total-elements }}'
})
每位开发者在合并前都能看到自己的变更对漂移的影响。架构文档与测试、代码检查和安全扫描一起,成为 CI 管道中的一等公民。
MCP:知道自身准确度的 AI 代理
如果你使用 Claude Code、Cursor 或任何 MCP 兼容的 AI 代理配合 Archyl 的 MCP 服务器,漂移评分可作为工具使用:
compute_drift_score({ projectId: "..." })
get_drift_score({ projectId: "..." })
get_drift_history({ projectId: "..." })
get_drift_details({ scoreId: "..." })
这意味着 AI 代理可以在开始工作之前检查文档准确度。get_agent_context 工具已经提供完整的 C4 model、ADR 和合规规则。现在它还可以检查这些文档有多可信。
看到 45% 漂移分数的代理知道应该对收到的架构上下文保持谨慎。看到 95% 的代理可以自信地依赖文档化的结构。这是自我感知 AI 代理的基础——它们根据文档质量调整自身行为。
Webhook 警报:在漂移发生时获得通知
两个新的 Webhook 事件让你无需查看仪表板就能保持信息同步:
drift.score_computed— 每次漂移分数计算完成时触发。推送到 Slack 频道以提高可见性。drift.score_degraded— 当分数比上次计算下降 10 分或更多时触发。这是你的预警系统——架构正在快速漂移。
在 Archyl 的 Webhook 设置中配置这些事件。它们支持 Slack、Microsoft Teams、Discord 以及任何通用 HTTP 端点。
REST API
对于需要完全编程控制的团队:
# 触发计算
curl -X POST https://api.archyl.com/api/v1/drift/compute \
-H "X-API-Key: $API_KEY" \
-H "X-Organization-ID: $ORG_ID" \
-H "Content-Type: application/json" \
-d '{"projectId": "your-project-uuid"}'
# 获取最新分数
curl https://api.archyl.com/api/v1/drift/latest?projectId=...
# 获取分数历史
curl https://api.archyl.com/api/v1/drift/history?projectId=...&limit=20
计算是异步的——POST 立即返回一个分数 ID,你轮询直到 status 变为 completed。GitHub Action 会自动处理这一切。
这个分数在闭环中的位置
分数是一个循环中的一步:代理和人读取模型,代码发生变化,分数衡量差距,CI 守住阈值,团队做出对齐。缺了测量这一步,这个循环就没有反馈,文档会在无人质疑的情况下继续漂移。这个论点,以及为什么要检测漂移的其余理由,都在指南里。
这篇文章要负责的,是让测量这一步值得信任。所以才有了公式、排除规则,以及它看不到的那四件事。
开始使用
- 在 Archyl 中打开任意项目
- 点击顶部工具栏的心跳图标
- 点击 "Compute Drift Score"
- 设置 GitHub Action 进行持续监控
- 配置 Slack Webhook 接收
drift.score_degraded警报
你的架构文档要么反映现实,要么不反映。现在你有了一个数字来告诉你到底是哪种情况,也有了足够的算术来跟它较真。
本系列的其余部分:架构漂移检测讲问题本身和其他检测方法,活的架构文档讲让分数不再滑落的实践。术语定义:架构漂移。产品页面:漂移检测。