每天三篇 arXiv cs.SE 论文 · 讲给不懂这个领域的人

← 全部论文

代码度量是真是假

用统计学的照妖镜,检验那些流行的代码质量指标到底测的是什么

Hera Arif, Miikka Kuutila, Paul Ralph · 26.09.17 · arXiv:2609.20411

1问题

想象一下,你刚接手一个几百兆的代码库,心里发慌:这玩意儿稳不稳定?你能不能放心改?于是你打开工具,屏幕上跳出一排数字:圈复杂度15,行数800,耦合度4。这些数字看起来像权威的诊断书,告诉你这块代码“病得不轻”。但问题是,这些指标真的在衡量我们以为的那些东西吗?比如,那个叫“圈复杂度”的数字,它测的到底是逻辑有多绕,还是作者写得有多啰嗦?如果指标不准,基于这些数字做出的重构决策,就是在盲人摸象。

2翻译

为了解开这个疑惑,作者没有去发明新指标,而是做了一次彻底的“体检”,看看现有的身体成分是否达标。他们从Apache Maven这个巨大的开源项目中提取了33个常见的类级别指标,然后用了两种统计学的大招——探索性因子分析(EFA)和验证性因子分析(CFA)。这就像是先给一堆数据做聚类,看看哪些数字天生抱团,再拿另一组独立数据去验证这个抱团关系牢不牢固。

结果出来之后,情况比预想的要复杂得多。首先,那33个指标并没有全部站住脚。有10个指标彻底“失联”,它们既不属于高内聚,也不属于低耦合,更像是随机噪音,被直接踢出了队列。剩下的23个指标里,又有10个在验证环节表现疲软,载荷太低,说明它们测的东西不够纯粹。

最终留下的,是24个经过淬炼的指标。但这还不是最反直觉的发现:传统的四大支柱——大小、内聚、继承、耦合——其中两个发生了裂变。原本我们认为是一个整体的“耦合”,其实包含了两条平行线:入向耦合(别人引用我多少)和出向耦合(我依赖别人多少),这两者在数学上并不混同。同样,“继承”也分成了子类相关和父类相关。而那个号称核心指标的“复杂度”,在这些类级别的维度里竟然没找到明确的位置,暗示它可能需要跨层级的视角才能看清。

为了让你更直观地看到差距,我们看几组关键数字:1. 废弃率:33个初始指标中,近三分之一(10个)因无对应维度被剔除,另有约30%(10个)因效力不足被淘汰,总淘汰率接近60%。2. 结构清晰度:最终模型解释了剩余指标70%以上的变异,证明留下的指标确实有清晰的归属。3. 维度分裂:在保留的24个指标中,超过半数(15个左右)归属于Coupling和Inheritance这两个多维构念,而非单一的Coupling或Inheritance概念。

这篇论文有一个值得注意的反直觉副发现:大多数开发者认为“坏味道”可以通过单一数值量化,但统计学告诉我们,很多所谓的单一属性,本质上是多个独立概念的混合物。如果不拆分,我们就无法精准诊断。

3核心概念

为了让这个故事立得住,我们要拆解三个主角手中不可少的零件。

第一,构造效度(Construct Validity)。这是整个故事的灵魂。简单说,就是“你量的东西,是不是你想量的东西”。比如你想量一个人的身高,却拿了一把秤(体重计),这就没有构造效度。在代码领域,如果一个“复杂度”指标实际上受变量命名长度的影响大于逻辑分支的影响,那它的构造效度就有问题。作者的核心工作就是把这些模糊的定性描述,变成可检验的定量假设。

第二,探索性因子分析与验证性因子分析(EFA/CFA)。这是作者手里的显微镜和X光机。EFA是不带预设地图的探险,把数据丢进去,让算法自动发现哪些指标喜欢聚在一起。比如EFA可能发现指标A和指标B总是一起高低变化,就把它们归为一类“耦合”。CFA则是拿着EFA绘制的地图,去新的实地(22个随机选中的项目)打卡验证。如果在新地图上迷路了,说明旧地图画错了。这种双剑合璧,是为了避免“过拟合”自家数据的巧合。

第三,多维构念(Multidimensional Constructs)。这是故事的高潮部分,也是设计的核心选择。传统思维倾向于把“耦合”看作一个标量(Scalar),即一个单一数值。但作者通过数据揭示,耦合其实是一个向量(Vector),包含指向外部(Out-Coupling)和接收外部(In-Coupling)两个正交维度。同理,继承也是分方向的。这一设计选择的理由是:软件系统的依赖关系天然具有方向性和非对称性,强行合并会导致信息丢失和诊断失真。只有承认其多维性,才能精准定位是“被依赖太多导致脆弱”还是“依赖太多导致沉重”。

4洞见

代码指标不是真理,而是需要不断校准的近似值;许多看似单一的代码特质,实则是多个独立维度的混合体,不拆分就无法测量。

5博导审稿

选题眼光不错。这个问题虽然老,但以前没人认真做过严格的统计学校验,大家都是在凭经验跑指标,属于典型的“手里有锤子,看啥都像钉子”。方法成熟度较高,引入现代计量理论的框架非常扎实,EFA+CFA的组合拳打得漂亮,避免了拍脑袋结论。

但实验诚意略有瑕疵。作者仅使用了Maven作为探索集,虽然用了另外22个项目验证,但这22个项目的规模和质量分布是否与Maven高度同质?如果Maven本身是一个极度规范的项目,那么得出的“有效指标”可能在混乱的商业代码中失效。此外,对于被剔除的那10个“噪音”指标,论文未深入探讨它们在特定语境下是否可能有特殊用途,一刀切删除略显武断。

写作功力中规中矩,逻辑链条完整,但在解释为何“复杂度”未能单独成类时,缺乏对现有复杂度定义(如McCabe)与类级别结构之间语义鸿沟的深刻讨论。

判决:Weak Accept。理由是方法严谨,结论对度量理论有修正意义,但外部效度(通用性)仍需更多样化的数据集支持。建议后续研究在异构项目集中复现这一因子结构。

6启发

这意味着你可以:1. 迁移:在你自己的静态代码分析引擎中,不要只输出总分。尝试将“耦合”拆分为输入依赖和输出依赖两个独立看板,观察两者的不同趋势。如果两者背道而驰,可能意味着不同的架构风险。2. 混搭:将你现有的“圈复杂度”与新的“多维耦合”指标结合。通常圈复杂度高的类往往伴随高内聚,但如果高圈复杂度配合高出向耦合,这就是高危预警区,优先级高于其他组合。3. 反转:默认假设是“指标越多越准”。但这篇论文告诉你,指标多等于噪声多。在你的团队推广新指标前,先问自己:这个数字背后的方差,有多少是由我想测量的那个抽象属性贡献的?如果不能回答,就不要把它写进日报里。