软件架构度量是很多团队在项目进入中期后才会认真面对的问题。功能开发阶段,架构好坏被需求速度掩盖;进入维护期后,模块之间依赖混乱、循环调用增多、改动一个接口牵连多个服务,这些现象往往没有量化依据,只能靠架构师的经验判断。软件架构度量的目标,就是把“架构是否健康”从主观描述变成可采集、可比对、可监控的数据。
本文会围绕软件架构度量这个主题,先讲清楚它到底在度量什么,再给出可落地的三层指标体系,并结合 Android APK CPU 架构检查、统信系统安装 deb 包架构不匹配、可信启动 TPM 度量过程等具体场景,提供一套从建立基线、接入 CI 到治理闭环的可操作方案。适合后端架构师、技术负责人、质量保障人员,以及希望给项目建立长期架构约束机制的开发同学。
1. 先理清软件架构度量到底在度量什么
1.1 一句话理解软件架构度量
软件架构度量,是用可量化的指标描述软件系统在结构、运行、质量属性和交付产物层面的健康程度。它的核心不是“给架构打分”,而是回答三类问题:
- 架构是否按照设计演进?
- 架构是否存在系统性风险?
- 架构变更是否符合预期?
如果只是说“这个模块越来越乱了”“这个系统不太稳”,那叫印象,不叫度量。度量的前提是指标可采集、阈值可比较、结果可追溯。有了这些,架构治理才能脱离个人经验,变成团队可以共同维护的流程。
1.2 度量的对象:不只是代码结构
很多开发者提到架构度量,第一反应是看耦合度、圈复杂度、代码行数。这些确实是度量的一部分,但架构的影响范围远不止源代码。
这里需要区分三个层面:
| 度量层面 | 典型对象 | 回答的问题 |
|---|---|---|
| 质量属性层 | 性能、可用性、可维护性、安全性 | 系统是否满足非功能需求 |
| 结构性度量层 | 模块依赖、循环依赖、耦合度、内聚度 | 架构是否按设计持续演进 |
| 产物与运行层 | 二进制包架构、依赖兼容、启动度量 | 交付产物是否和目标环境匹配 |
线上故障里有一类很典型:代码层面没有任何问题,但交付的 APK 不包含目标设备的 CPU 架构,或者从统信应用商店导入的 deb 包架构与系统不匹配,导致安装失败。这类问题本质上就是架构度量的缺失——没有在产物层面校验架构属性。
1.3 软件架构度量不等于代码度量
代码度量的粒度是类、方法、变量,关注的是“这段代码写得好不好”。软件架构度量的粒度是模块、服务、组件、二进制产物,关注的是“这些代码组合起来形成的系统是否健康”。
一个类写得再规范,如果模块之间存在循环依赖、服务之间通过隐式配置互相拉扯、客户端安装包携带了错误的 CPU 架构标识,最终表现仍然是一个不健康的系统。
所以软件架构度量应该站在比代码度量更高的层级去看问题。它的输入来自多个渠道:静态代码分析、构建产物检查、运行时监控、安全启动日志,甚至是包管理器的依赖解析结果。
2. 软件架构度量的三层指标体系
2.1 质量属性度量:先明确“架构好不好”的业务视角
架构好不好,首先取决于它是否满足业务方的非功能需求。同一个系统,在金融场景和内容社区场景,对可用性和安全性的要求完全不同,指标体系也不一样。
质量属性度量通常围绕以下维度展开:
| 维度 | 常见指标 | 示例 |
|---|---|---|
| 性能 | 响应时间、吞吐量、P99 延迟 | 订单接口 P99 小于 800ms |
| 可用性 | SLA、MTTR、故障恢复时间 | 月度可用性不低于 99.95% |
| 可维护性 | 架构符合率、模块变更影响范围 | 核心模块规则测试通过率 100% |
| 安全性 | 漏洞密度、权限模型复杂度 | 高危接口全部经过鉴权 |
| 可移植性 | 多架构支持、多平台适配 | APK 同时支持 arm64-v8a 与 armeabi-v7a |
这些指标需要团队根据业务目标提前定义,否则无法判断一个架构决策是“优化”还是“退化”。例如从单体拆成微服务后,可用性是否真的提升了,不能只看系统能否启动,而要看故障爆炸半径是否缩小、发布频率是否提升、单个服务故障时业务是否还能部分可用。
2.2 结构性度量:耦合、内聚、循环依赖与扇入扇出
结构性度量是软件架构度量中最成熟的部分。它回答的是“模块之间的组织方式是否合理”。
常用指标包括:
- 耦合度:一个模块依赖其他模块的程度。耦合过高时,模块复用困难,变更容易波及外部。
- 内聚度:模块内部元素之间的关联程度。内聚度低,说明模块职责分散,容易变成“杂货袋”。
- 循环依赖:A 依赖 B,B 又依赖 A。循环依赖会破坏编译和运行时的清晰方向,也是架构腐化的典型信号。
- 扇入与扇出:扇入指当前模块被多少个模块依赖,扇出指当前模块依赖多少个外部模块。扇入高说明模块复用价值高;扇出高说明模块依赖复杂,需要重点审查。
- 架构符合率:用架构测试工具定义模块边界,检查真实依赖关系是否越过边界。
在 Java 服务端项目中,可以用 ArchUnit 这类工具把架构规则变成自动化测试。下面是一个最小示例,用于检查controller包不能直接依赖repository包:
@AnalyzeClasses(packages = "com.example.demo") public class ArchitectureRuleTest { @Test void controllerShouldNotDependOnRepository() { JavaClasses importedClasses = new ClassFileImporter().importPackages("com.example.demo"); ArchRule rule = noClasses() .that().resideInAPackage("..controller..") .should().dependOnClassesThat() .resideInAPackage("..repository.."); rule.check(importedClasses); } }这段代码解决什么问题?它把架构约束从文档变成了持续执行的检查。只要有代码提交触发了这条测试,CI 就会立即发现跨层依赖,而不是等问题在联调阶段暴露。
注意:架构规则测试不是越多越好。规则过多、边界定义过于琐碎,会导致每次提交都在改规则配置,团队最终会失去维护意愿。建议从最影响交付质量的依赖方向开始,比如核心领域层不许依赖基础设施层。
2.3 产物与运行时度量:用 CPU 架构检查看清构建产物
软件架构度量不能只停留在源代码层面。构建产物与目标运行环境的匹配程度,同样属于架构度量的一部分。
安卓应用就是一个典型例子。Android 应用通过 JNI 加载.so动态库时,.so文件必须与设备的 CPU 架构匹配。如果 APK 只打包了 arm64-v8a 的 so,在高通骁龙设备上可以运行,在老式只支持 armeabi-v7a 的设备上就会崩溃;如果只打包了 armeabi-v7a,在部分门店测试机或模拟器上又可能出现指令集不兼容。
查看 APK 支持的 CPU 架构,最直接的方法是使用 Android SDK 自带的 aapt 工具:
aapt dump badging app-release.apk | grep native-code正常输出类似:
native-code: 'arm64-v8a' 'armeabi-v7a' 'x86_64'如果 CDN、加固平台或构建脚本改动了 APK,或者 so 没有正确打进 lib 目录,可以通过解压 APK 检查:
unzip -l app-release.apk | grep "lib/"输出中应该能看到架构目录:
lib/arm64-v8a/libnative-lib.so lib/armeabi-v7a/libnative-lib.so lib/x86_64/libnative-lib.so进一步确认单个 so 文件的真实架构,可以用 readelf 检查 ELF 头部:
readelf -h lib/arm64-v8a/libnative-lib.so | grep -E "Class|Machine"预期输出:
Class: ELF64 Machine: AArch64在 CDN 下载、渠道包生成、SDK 集成这些环节里,建议都加上产物架构校验。这是软件架构度量在交付侧的落地方式,不要等到用户设备上崩溃了再排查。
2.4 平台适配度量:软件包架构不匹配的排查案例
和安卓 APK 类似的场景,在 Linux 桌面系统中同样常见。统信系统、Ubuntu、Debian 系发行版安装 deb 包时,如果软件包架构和系统架构不一致,包管理器会直接拒绝安装。
现象通常是这样的:从外部渠道下载了一个安装包,双击安装后提示“软件包架构不匹配”,或者命令行安装时报:
dpkg: error: package architecture (arm64) does not match system (amd64)排查顺序如下:
先确认系统当前架构:
dpkg --print-architecture如果是 amd64,再查看系统是否开启了多架构支持:
dpkg --print-foreign-architectures然后查看 deb 包声明的架构:
dpkg -I ./your-package.deb | grep Architecture如果 deb 包是 arm64,系统是 amd64,那就需要去官方渠道下载 x86_64 或 amd64 的对应版本,而不是强行修改架构标志。
这里要特别说明:不要在 x86_64 系统上通过dpkg --force-architecture强制安装 arm64 包。即使安装成功,应用也可能因为缺少对应架构的动态依赖而无法运行,反而把第一个问题变成更难排查的问题。
在软件架构度量的视角下,这类问题属于“交付产物与目标平台架构不匹配”。规范的团队应该在 CI 打包阶段就把当前产物支持的架构声明出来,并和安装目标平台的架构做自动比对。
2.5 可信启动中的“度量”指的是什么
搜索材料里出现“tpm 度量过程”时,很多做应用开发的同学会困惑:这和架构度量是什么关系?
TPM 是可信平台模块,它做的是“启动链路度量”。在可信启动流程中,TPM 会依次对固件、引导加载程序、操作系统内核等启动组件的代码进行哈希度量,并把度量值记录到平台配置寄存器(PCR)中。后续阶段可以校验这些值,确认启动链路上的每一个环节是否被修改。
这里的“度量”和软件架构度量不同。TPM 度量关注的是运行环境的可信链,而软件架构度量关注的是系统结构和交付产物的健康度。但两者有一个共同点:都强调“先建立基准,再校验偏差”。没有基准的度量,在架构治理中同样没有意义。
另外,如果搜索时看到“等度量映射”,那属于机器学习降维算法领域,和软件架构度量不是同一个概念。在架构度量讨论中,“度量”指的是对架构特征的量化和评估,不是对数据进行维数约减。
3. 从零建立一套架构度量基线:以依赖和产物校验为例
3.1 明确范围:不要一开始就度量所有东西
架构度量最常见的失败方式,是想把性能、可用性、耦合、安全、产物架构一次性全部纳入度量体系。结果指标表很完整,但数据源不稳定、责任人不明确,一个月后所有指标都停在“待采集”状态。
推荐做法是从最痛的点开始。可以先问自己三个问题:
- 当前最频繁出现的线上问题,是依赖混乱导致的,还是产物架构不匹配导致的?
- 团队是不是经常在合并请求阶段争论“这个依赖能不能加”?
- 交付测试包时,是不是经常出现“安装不了”“这个包在某个设备上崩溃”的反馈?
如果答案是肯定的,就围绕这些问题建立第一版度量基线。下面示例以“模块依赖控制”和“APK 产物架构校验”两个方向为例。
3.2 定义指标与初始阈值
指标定义要能回答以下问题:
| 指标名 | 计算公式或判定方式 | 初始阈值 | 违反后的动作 |
|---|---|---|---|
| controller 到 repository 的直接依赖 | 统计依赖数量,应为 0 | 0 | CI 失败 |
| 模块循环依赖数 | 通过依赖分析工具统计强连通分量 | 0 | CI 失败 |
| 架构测试通过率 | 通过的架构规则数 / 全部规则数 | 100% | 阻断合并 |
| APK native-code 架构标识 | aapt 输出是否包含预期架构 | 必须包含 arm64-v8a | 阻断发布 |
| deb 包架构与目标系统匹配 | dpkg -I 输出与目标架构对比 | 必须匹配 | 阻断发布 |
阈值不能凭空拍脑袋。如果项目目前已经存在 5 个循环依赖,短期目标是让新代码不再增加,中期目标才是逐步降为 0。所以第一版阈值要和现状平行对比,而不是直接套用“理想架构”的标准。
3.3 采集数据并生成基线报告
依赖关系数据可以使用 jdeps、ArchUnit、NDepend、Structure 101 等工具采集。以 Java 项目的 jdeps 为例,可以快速看到模块之间的包依赖:
jdeps --module-path target/classes -s target/classes输出类似:
com.example.demo.controller -> com.example.demo.service com.example.demo.service -> com.example.demo.repository com.example.demo.domain -> java.base把这些输出整理成基线报告,提交到版本库。后续每次架构变动,都用新报告和基线对比,任何新增的跨层依赖都能被感知。
APK 产物架构校验可以在构建脚本里加入以下逻辑:
if ! aapt dump badging app-release.apk | grep -q "arm64-v8a"; then echo "APK does not support arm64-v8a" exit 1 fi这套检查不需要额外部署服务,只需要 Android SDK build-tools 里有 aapt,适合作为 gradle 构建后的任务。
3.4 把基线报告纳入版本管理
架构基线报告和代码一样,需要纳入版本管理。建议在仓库中建立architecture/目录:
architecture/ baseline/ dependency-report.txt apk-arch-report.txt rules/ architecture-rule-test.java每次架构评审、重构、依赖升级之后,都要重新生成基线报告,并提交差异说明。没有版本历史的度量,无法回答“这个指标什么时候开始变差的”;有了版本历史,才能把指标波动和代码变更关联起来。
4. 把架构度量接入开发流程,形成质量门禁
4.1 本地开发阶段:合并请求前跑一次架构检查
架构度量要真正发挥作用,不能只靠月底汇总,而要在开发闭环中提前拦截。最简单的做法是在提交代码前执行架构测试命令。
Maven 项目可以只运行架构规则相关的测试:
mvn test -Dtest=ArchitectureRuleTestGradle 项目类似:
./gradlew test --tests "com.example.ArchitectureRuleTest"本地检查的目的不是替代 CI,而是让开发者在提交之前就知道自己的改动是否破坏了架构边界。很多团队把架构测试混在几千个单元测试里,开发跑全量测试太慢,就养成了“本地不跑直接推送”的习惯。建议把架构测试单独拆成一个 test class,并设置专门的 task,方便在本地快速执行。
4.2 CI 阶段:架构测试与产物校验并行
CI 阶段需要同时处理两类指标:
- 结构性指标:架构规则测试、依赖分析、循环依赖检测。
- 产物指标:APK 的 native-code 架构、deb 包架构、Docker 镜像平台声明。
Jenkins 流水线可以这样拆分阶段:
stage('Architecture Check') { steps { sh 'mvn test -Dtest=ArchitectureRuleTest' } } stage('Artifact Architecture Check') { steps { sh 'bash scripts/check-apk-arch.sh' sh 'bash scripts/check-deb-arch.sh' } }check-apk-arch.sh的核心逻辑就是解析构建产物,校验声明的架构是否满足目标设备矩阵:
#!/usr/bin/env bash set -euo pipefail APK_PATH="${1:-build/outputs/apk/release/app-release.apk}" EXPECTED_ARCH="${2:-arm64-v8a}" if ! aapt dump badging "$APK_PATH" | grep -q "native-code:.*${EXPECTED_ARCH}"; then echo "架构检查失败: APK 缺少 ${EXPECTED_ARCH} 支持" exit 1 fi echo "架构检查通过: ${APK_PATH}"4.3 发布阶段:做最终制品确认
很多架构问题不会在 CI 早期暴露,而是在最终联调包、预发包、商店上架包上才体现。原因是构建脚本可能在不同阶段执行不同的 task,到了 release 阶段,so 文件过滤规则被改动、CDN 覆盖了制品、加固平台重新打包,都会导致产物丢失架构。
因此发布前需要做最终制品确认,而不只是相信早期构建结果:
- 从制品库拉取最终 APK,重新执行 aapt 校验。
- 用 unzip 检查 lib 目录下是否存在对应架构目录。
- 在目标架构的低端真机上做冒烟测试,而不是只在模拟器上验证。
- 对于 Linux 桌面系统,用
dpkg -I检查发布包架构,并确认系统源里存在对应依赖。
发布阶段还应该保留一份架构校验结果,作为该版本的交付记录。后续如果用户反馈“某个设备打不开”,可以直接从记录中确认该版本是否支持对应架构,节省大量排查时间。
4.4 分级告警:哪些必须拦截,哪些只提示
架构度量接入流程后,要警惕“所有指标都变成门禁”的极端情况。一旦每个指标都阻断发布,团队就会习惯性绕过规则,甚至删除架构测试。
建议将指标按严重程度分级:
| 级别 | 示例 | 处理策略 |
|---|---|---|
| 致命 | 核心领域层依赖基础设施层、安装包缺少目标架构 | 强制拦截,不能合并,不能发布 |
| 警告 | 模块扇出超过预警值、依赖数量短期增长过快 | 记录告警,允许合并,但需要负责人跟进 |
| 参考 | 重复代码率、类长度、包体积 | 周报展示,不设硬性门禁 |
关键原则是:门禁数量要少,每条门禁必须有明确负责人。如果一条规则被绕过三次,就需要重新评估它是不是当前阶段最重要的约束。
注意:架构度量接入 CI 后,告警必须能落到具体负责人。否则会出现“指标红了三天,但没人在意”的局面。建议在告警通知里带上最近一次违反规则的文件路径和提交人,减少定位成本。
5. 架构度量常见的误区和排查路径
5.1 指标全部变红,但系统看起来运行稳定
这是很常见的困惑:耦合度上升了、循环依赖变多了、架构测试也失败了,但线上业务没有任何异常。
理解这个现象的关键是:结构性指标是“慢变量”,它的影响不会立刻通过一次接口报错体现,而是会积累到下一次大规模重构、紧急升级、新人接手维护时才爆发。架构度量不是预测明天是否宕机,而是评估未来变更成本。
排查时不要只看单个指标,要看趋势。
- 对比基线报告,确认变红发生在哪个提交。
- 看模块图,确认新增依赖是否位于核心链路。
- 评估这些变化是否为了支撑合理业务需求。
如果新增依赖是合理的,那就更新基线,而不是一味要求“恢复绿色”。架构度量服务于决策,不是服务于指标本身。
5.2 只统计平均值,导致子系统退化被掩盖
有的团队每月统计一次“平均模块依赖数”,发现数字只涨了一点点,就认为架构稳定。但平均值很容易被少数大模块稀释。
比如系统有 100 个模块,其中 99 个模块的依赖数都在合理范围,只有核心结算模块的依赖数从 20 涨到了 200。平均下来上涨幅度可能不到 2,但实际风险已经非常严重。
正确做法是同时关注分位数和极端值:
- P50、P90、P99 模块依赖数。
- 依赖数最高的前 10 个模块。
- 循环依赖涉及的类数量和文件数量。
在架构报告里,用“Top 异常模块”比单纯的总量表更有信息量。这也是为什么建议每次度量都生成完整报告,而不是只汇总一个平均分。
5.3 手工采集架构指标,两周后没有人再更新
有些团队接入了架构工具,但数据采集还是靠“需要的时候手动跑一次”。这种模式的问题在于:手工流程一定会被遗忘。尤其是项目进入高迭代期后,连代码评审都可能简化,更不会有人主动去跑依赖分析。
解决办法是把采集命令写进 CI 或提交钩子,让数据自动生成、自动归档。人只负责看结果,不负责触发过程。
5.4 度量数据生成后没人看
架构报告生成后,如果不进入任何决策流程,那它就只是一份存档文件。最常见的原因是:报告没有和评审、迭代计划、上线清单绑定。
建议把架构度量结果放入以下三个场景:
- 合并请求描述中贴上架构测试执行结果。
- 迭代评审时用 5 分钟过一遍架构趋势。
- 发布检查单中加入“产物架构校验是否通过”这一项。
当度量数据成为团队协作材料时,它才真正具备治理作用。
5.5 排查顺序:从数据到决策
遇到架构相关质疑时,可以按以下顺序排查:
- 确认数据是否准确:报告是什么时间生成的?工具版本有没有变化?是不是误报了?
- 确认指标含义是否一致:全团队对“循环依赖”的定义是否相同?统计口径有没有调整?
- 对比基线:相比上次报告,变化出现在哪些模块?触发变更是功能迭代还是重构?
- 评估影响范围:变化是否触碰核心链路?影响哪些下游模块?
- 做出决策:是修复还是更新基线?由谁负责、何时完成?
这条链路和线上故障排查的本质一致,都是先排除数据噪声,再定位变更源头,最后给出处理方案。
6. 软件架构度量的落地建议与扩展方向
6.1 推荐的落地路线图
团队从零建设架构度量时,可以按以下阶段推进:
第一阶段:跑通单项度量。
选择最痛的一块,比如“核心模块不允许反向依赖”,用 ArchUnit 或类似工具写成测试,跑通 CI。这一步能最快建立起团队对架构度量的认知。
第二阶段:建立基线和趋势。
把依赖分析、产物架构校验的结果纳入版本库,形成周级或迭代级趋势。重点是让团队看到数字变化和代码变更的对应关系。
第三阶段:接入流程门禁。
把关键规则加入合并请求检查、发布检查单。同时建立分级告警机制,避免门禁过多导致规则被绕过。
第四阶段:走向治理闭环。
每次架构评审会议以度量报告为基础,先看数据变化,再讨论方案。措施执行后,在下一次报告中验证效果。
6.2 可复用的架构度量检查清单
以下清单可以直接作为项目落地前的最小集:
基础项
- [ ] 是否定义了 3 到 5 个核心架构指标,而不是追求大而全?
- [ ] 每个指标是否有明确数据来源和采集命令?
- [ ] 是否有初始阈值和分级策略?
- [ ] 是否有指标负责人?
结构项
- [ ] 是否检查核心模块之间不允许出现循环依赖?
- [ ] 是否定义了 layer 依赖方向,并用自动化测试强制?
- [ ] 新模块加入时,是否更新依赖报告?
- [ ] 重构或依赖升级后,是否重新生成基线?
产物项
- [ ] APK 构建后是否校验 native-code 架构?
- [ ] deb 包发布前是否确认目标系统架构?
- [ ] 制品库中的最终包是否重新校验,而不是只信任构建日志?
- [ ] 目标设备矩阵是否覆盖真机架构验证?
流程项
- [ ] 架构检查是否接入 CI?
- [ ] 架构测试是否能在本地单独快速执行?
- [ ] 告警通知是否包含文件路径和提交人?
- [ ] 度量报告是否进入迭代评审或发布检查单?
6.3 从架构度量走向架构治理
架构度量的最终目的不是生成一组数字,而是让架构决策从“凭感觉”变成“看数据”。当结构化指标和产物校验指标进入日常开发流程后,团队自然会对依赖方向、模块边界、平台适配形成约束意识。
更进一步,可以把架构度量和架构决策关联起来。某个模块的依赖增长达到预警值时,自动触发一次架构评审;某个 APK 缺失目标架构时,发布流水线直接阻断。技术负责人需要关注的不是每个指标的上下波动,而是例外情况的处理是否闭环。
6.4 适合继续深入的方向
软件架构度量本身是一个持续扩展的领域,后续可以从以下方向继续深入:
- 运行时架构度量:通过链路追踪数据观察服务调用关系是否与设计一致。
- 安全架构度量:把认证授权模型、数据访问边界、可信启动检查纳入度量范围。
- 数据架构度量:分析表之间外键依赖、领域模型的聚合边界。
- AI 辅助的架构分析:利用大模型对架构文档、代码结构、依赖报告做一致性分析。
对新手来说,最值得先练的是把“发现一个架构痛点”到“建成一条自动化规则”走通一次。不需要一开始就建完整的度量平台,从一个模块、一条规则、一次产物校验开始,持续运行两周后,再扩展范围。这样建立起来的架构度量体系,才是在真实项目里能活下来的体系。