86

我最近在 SO 上看到了许多与“代码指标”相关的问题,我想知道它的魅力是什么?以下是一些最近的例子:

不过,在我看来,没有任何指标可以替代代码审查:

  • 某些指标有时可能表明需要审查的地方,并且
  • 短期内指标的根本变化可能表明需要审查的地方

但是我想不出一个单独的指标本身总是指示“好”或“坏”代码 - 总是有例外和测量无法看到的事情的原因。

从我忽略的代码指标中是否有一些神奇的见解?懒惰的程序员/经理是否在寻找不阅读代码的借口?人们是否会看到巨大的遗留代码库并寻找起点?这是怎么回事?

注意:我已经在答案和评论中针对特定线程提出了其中一些问题,但没有得到任何回复,所以我认为我应该向整个社区提问,因为我可能遗漏了一些东西。运行一个指标批处理作业并且实际上不必再次阅读其他人的代码(或我自己的)会很好,我只是认为它不实用!

编辑:如果不是所有正在讨论的指标,我对大多数指标都很熟悉,我只是不认为它们的意义是孤立的,也不是任意的质量标准。

4

18 回答 18

87

这个线程中的答案有点奇怪,因为他们谈到:

  • “团队”,如上述指标的“唯一受益者”;
  • “指标”,就像它们本身意味着什么一样。

1/ 指标不是针对一个人群,而是针对三个人群:

  • 开发人员:他们关心关于代码静态分析的瞬时 静态代码指标(圈复杂度、注释质量、行数……)
  • 项目负责人:他们关心来自单元测试、代码覆盖率、持续集成测试的日常 实时代码指标
  • 业务发起人(他们总是被遗忘,但他们是利益相关者,是为开发买单的人):他们关心每周 关于架构设计、安全性、依赖关系的全球代码指标......

当然,所有这三个群体都可以观察和分析所有这些指标,但每种指标都旨在为每个特定群体更好地使用。

2/ 指标本身就代表了代码的快照,这意味着……什么都没有!

正是这些指标的组合以及这些不同分析级别的组合可能表明代码“好”或“坏”,但更重要的是,这些指标的趋势是重要的。

这是那些将赋予真正附加值的指标的重复,因为它们将帮助业务经理/项目负责人/开发人员在不同的可能代码修复中确定优先级


换句话说,您关于“指标的魅力”的问题可能指的是:

  • “漂亮”的代码(尽管它总是在旁观者的眼中)
  • “好”代码(有效,并且可以证明它有效)

因此,例如,一个圈复杂度为 9 的函数可以被定义为“美丽的”,而不是一个圈复杂度为 42 的长卷积函数。

但是,如果:

  • 后一个函数具有稳定的复杂性,加上95% 的代码覆盖率,
  • 而前者的复杂性越来越高,而且覆盖率... 0%,

有人可能会争辩说:

  • 后者代表一个“”的代码(它可以工作,它很稳定,如果需要更改,可以检查修改后是否仍然有效),
  • 前者是一个“糟糕”的代码(它仍然需要添加一些案例和条件来覆盖它必须做的所有事情,并且没有简单的方法来进行一些回归测试)

所以,总结一下:

一个单一的指标,它本身总是表明 [...]

:不多,只是代码可能更“漂亮”,这本身并不意味着很多......

从我忽略的代码指标中是否有一些神奇的见解?

只有指标的组合趋势才能提供您所追求的真正“神奇洞察力”。

于 2008-10-12T19:49:51.827 回答
22

一个月前,我有一个项目,我作为一个人的工作来测量圈复杂度。那是我第一次接触这些指标。

我收到的第一份报告令人震惊。几乎我所有的功能都没有通过测试,即使是(恕我直言)非常简单的功能。我通过将逻辑子任务移动到子例程中来解决复杂性问题,即使它们只被调用一次。

对于另一半例程,我作为一名程序员的自豪感开始了,我尝试以它们相同的方式重写它们,只是更简单,更易读。这很奏效,我能够最大限度地降低客户的复杂性阈值。

最后,我几乎总能想出更好的解决方案和更简洁的代码。性能并没有因此受到影响(相信我——我对此很偏执,我经常检查编译器输出的反汇编)。

如果您将指标用作改进代码的理由/动机,我认为指标是一件好事。不过,知道何时停止并请求违反指标的授权是很重要的。

指标是指导和帮助,而不是目的本身。

于 2008-10-12T19:51:39.967 回答
16

我用过的最好的指标是CRAP 分数

基本上,它是一种将加权圈复杂度与自动测试覆盖率进行比较的算法。该算法如下所示:CRAP(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m) 其中 comp(m) 是方法 m 的圈复杂度,cov(m) 是自动化测试提供的测试代码覆盖率。

上述文章的作者(请去阅读它......这非常值得你花时间)建议最高 CRAP 分数为 30,其分解方式如下:

Method’s Cyclomatic Complexity        % of coverage required to be
                                      below CRAPpy threshold
------------------------------        --------------------------------
0 – 5                                   0%
10                                     42%
15                                     57%
20                                     71%
25                                     80%
30                                    100%
31+                                   No amount of testing will keep methods
                                      this complex out of CRAP territory.

正如您很快看到的那样,该指标奖励编写不复杂的代码以及良好的测试覆盖率(如果您正在编写单元测试,并且您应该并且没有测量覆盖率......好吧,您可能会喜欢随风而行以及)。;-)

对于我的大多数开发团队,我非常努力地使 CRAP 分数低于 8,但如果他们有正当理由证明增加的复杂性是可以接受的,只要他们通过足够的测试覆盖了复杂性。(编写复杂的代码总是很难测试......这个指标的一种隐藏的好处)。

大多数人一开始发现很难编写能够通过 CRAP 分数的代码。但随着时间的推移,他们编写了更好的代码,问题更少的代码,以及更容易调试的代码。在任何指标中,这是关注最少且收益最大的指标。

于 2010-07-27T17:06:02.757 回答
11

对我来说,识别不良代码的最重要的指标是圈复杂度。我项目中的几乎所有方法都低于 CC 10,并且在 CC 超过 30 的旧方法中总是会发现错误。高 CC 通常表示:

  • 匆忙编写的代码(即没有时间找到一个优雅的解决方案,而不是因为问题需要一个复杂的解决方案)
  • 未经测试的代码(没有人为这种野兽编写测试)
  • 多次修补和修复的代码(即充斥着 ifs 和 todo 注释)
  • 重构的主要目标
于 2008-10-12T21:36:57.897 回答
9

好的代码审查不能代替好的静态分析工具,当然也不能代替好的单元测试,现在单元测试没有验收测试是不行的……

代码度量是放入工具箱的另一个工具,它们本身并不是解决方案,它们只是一个可以适当使用的工具(当然还有你工具箱中的所有其他工具!)。

于 2008-10-12T19:42:15.727 回答
6

人们被以机械方式理解和描述代码的理念所吸引。如果属实,请考虑对效率和生产力的影响!

我同意“代码质量”的衡量标准与“优秀散文”的衡量标准一样合理。然而,这并不意味着指标是无用的,只是可能被滥用了。

例如,某些指标的极端指出了可能出现的问题。1000 行长的方法可能无法维护。具有零单元测试代码覆盖率的代码可能比具有大量测试的类似代码具有更多错误。在发布之前添加到项目中的不是第三方库的代码的大幅跳跃可能会引起额外的关注。

我认为如果我们使用指标作为建议——一个危险信号——也许它们会很有用。问题是当人们开始用 SLOC 来衡量生产力或用测试线的百分比来衡量质量时。

于 2008-10-12T19:31:07.260 回答
6

我高度主观的观点是,代码度量表达了对能够量化本质上无法量化的事物的不可抗拒的制度魅力。

在某种程度上,至少在心理上是有道理的——你怎么能对你无法评估或理解的事情做出决定?最终,当然,除非您对该主题很了解(并且至少与您要评估的内容一样好)或询问知识渊博的人,否则您无法评估质量,这当然只会让问题回归一步。

从这个意义上说,也许一个合理的类比是通过 SAT 分数来评估大学入学者,这是不公平的,并且会遗漏各种细微之处,但是如果您需要量化,就必须做点什么。

并不是说我认为这是一个很好的衡量标准,只是我可以看到它的直觉性不可抗拒性。而且,正如您所指出的,可能有一些合理的指标(很多 500+ 行方法,高复杂性 - 可能很糟糕)。不过,我从来没有去过一个买这个的地方。

于 2008-10-12T19:31:50.083 回答
6

我相信一个代码指标。

我正在开发一个大系统。当我遇到一个新需求时,我开始着手编写代码。当我完成并解决了错误后,我将其签入版本控制系统。该系统会进行比较,并计算我所做的所有更改。

这个数字越小越好。

于 2008-11-22T14:50:37.543 回答
5

指标和自动化测试并不意味着要替代完整的代码审查。

他们只是加快速度。使用自动检查器,可以很容易地查看您忘记遵循哪些约定,您正在使用指定的包和方法等。您可以在不占用其他人时间的情况下查看可以修复的内容。

经理们也喜欢衡量他们,因为他们觉得他们得到了一个关于生产力的准确数字(尽管通常情况并非如此)并且他们应该能够更好地处理人们。

于 2008-10-12T19:31:25.123 回答
5

测量仅在以下情况下才有用:

  • 该团队开发了它们
  • 团队同意他们
  • 它们被用于识别特定区域

一般来说,任何不适合的指标都会受到团队优化的影响。您想测量代码行数吗?天哪,看看他们能写多少!你想测量代码覆盖率,天哪,看我覆盖那个代码!

我认为指标对于识别趋势很有用,事实上,我已经看到了一些有用的指标,例如绘制构建中断的时间、代码流失(整个项目中更改的代码行数)和其他东西。但是如果团队没有提出他们,或者他们不同意或不理解他们,那么你很可能会受到伤害。

于 2008-10-12T19:32:19.100 回答
5

这是来自stan4j的一些复杂性指标。

一个eclipse类结构分析工具。

我喜欢这个工具和指标。我将指标视为统计数据、指标、警告消息。有时由于某些方法或某些类确实有一些复杂的逻辑使它们变得复杂,应该做的是密切关注它们,检查它们是否需要重构它们或仔细检查它们,由于通常他们很容易出错。我也用它作为分析工具来学习源代码,因为我喜欢从复杂到简单的学习。实际上它包括其他一些指标,如 Robert C. Martin Metrics、Chidamber & Kemerer Metrics、Count Metrics 但我最喜欢这个

复杂性指标

圈复杂度度量

圈复杂度 (CC) 方法的圈复杂度是方法的控制流图中的决策点数加一。决策点出现在 if/for/while 语句、case/catch 子句和类似的源代码元素中,其中控制流不仅仅是线性的。单个(源代码)语句引入的(字节代码)决策点的数量可能会有所不同,这取决于例如布尔表达式的复杂性。一个方法的圈复杂度值越高,测试该方法控制流图的所有分支所需的测试用例就越多。

平均圈复杂度 圈复杂度指标在应用程序、库、包树或包的所有方法上的平均值。

Fat Metrics 人工制品的脂肪量度是人工制品的适当依赖图中的边数。依赖图类型取决于度量变量和选择的工件:

Fat 应用程序、库或包树的 Fat 度量是其子树依赖图的边数。此图包含包树层次结构中的所有工件的子项,因此也包括叶包。(要在合成视图中查看适当的图表,必须禁用结构资源管理器的平面包切换。如果所选工件是库,则必须启用显示库切换,否则必须禁用。)

包的 Fat 度量是其单元依赖图的边数。该图包含包的所有顶级类。

一个类的 Fat 度量是它的成员图的边数。该图包含该类的所有字段、方法和成员类。(此图和 Fat 值仅在使用 Level of Detail 成员而不是 Class 执行代码分析时可用。)

Fat for Library Dependencies (Fat - Libraries) 应用程序的 Fat for Library Dependencies 度量是其库依赖图的边数。此图包含应用程序的所有库。(要在合成视图中查看适当的图表,必须启用结构浏览器的显示库切换。)

Fat for Flat Package Dependencies (Fat - Packages) 应用程序的 Fat for Flat Package Dependencies 度量是其平面包依赖图的边数。该图包含应用程序的所有包。(要在合成视图中查看适当的图表,必须启用结构资源管理器的平面包切换,并且必须禁用显示库切换。)

一个库的 Fat for Flat Package Dependencies 度量是它的平面包依赖图的边数。该图包含库的所有包。(要在合成视图中查看适当的图表,必须启用结构浏览器的平面包和显示库切换。)

Fat for Top Level Class Dependencies (Fat - Units) 应用程序或库的 Fat for Top Level Class Dependencies 度量是其单元依赖图的边数。该图包含应用程序或库的所有顶级类。(对于合理的应用程序,它太大而无法可视化,因此无法在组合视图中显示。单元依赖关系图可能仅显示为包。)

于 2011-08-18T12:58:34.140 回答
2

度量标准可能有助于确定项目的改进或降级,并且肯定可以发现样式和约定违规,但没有什么可以替代同行代码审查。没有它们,您不可能知道代码的质量。

哦……这假设您的代码审查中至少有一个参与者有线索。

于 2008-10-12T19:33:00.643 回答
2

我同意你的观点,代码指标不应该代替代码审查,但我相信它们应该补充代码审查。我认为这又回到了那句老话“你无法改进你无法衡量的东西”。代码度量可以为开发团队提供可量化的“代码气味”或可能需要进一步调查的模式。大多数静态分析工具中捕获的指标通常是在我们领域的短暂历史中的研究过程中确定的具有重要意义的指标。

于 2008-10-12T19:37:06.890 回答
2

指标不能代替代码审查,但要便宜得多。它们比什么都重要。

于 2008-10-12T19:42:57.690 回答
2

部分答案是,一些代码指标可以让您快速、初步地回答以下问题:这段代码是什么样的?

甚至“代码行”也可以让您了解您正在查看的代码库的大小。

正如另一个答案中提到的,指标的趋势为您提供了最多的信息。

于 2008-10-12T21:02:36.670 回答
2

指标本身并不是特别有趣。重要的是你对他们所做的事情。

例如,如果您正在测量每行代码的评论数,您认为什么是好的值?谁知道?或者更重要的是,每个人都有自己的看法。

现在,如果您收集了足够的信息,以便能够将每行代码的注释数量与解决错误所需的时间或发现的归因于编码的错误数量相关联,那么您可能会开始找到一个经验上有用的数字.

在软件中使用度量和在任何其他过程中使用任何其他性能度量之间没有区别——首先测量,然后分析,然后改进过程。如果你所做的只是测量,那你就是在浪费时间。

编辑:回应 Steven A. Lowe 的评论 - 这是绝对正确的。在任何数据分析中,都必须小心区分因果关系和单纯的相关性。根据适用性选择指标很重要。尝试衡量咖啡消费量和归因代码质量是没有意义的(尽管我确信有些人已经尝试过;-))

但在找到关系(因果关系或非因果关系)之前,您必须拥有数据。

要收集的数据的选择基于您希望验证或改进的过程。例如,如果您尝试分析代码审查程序的成功(使用您自己的“成功”定义,减少错误或减少编码错误,或缩短周转时间等),那么您选择衡量的指标错误的总比率和审查代码中的错误率。

所以在你收集数据之前,你必须知道你想用它做什么。如果指标是手段,目的是什么?

于 2008-10-12T22:58:01.133 回答
2

我不认为指标的微小变化是有意义的:复杂度为 20 的函数不一定比复杂度为 30 的函数更干净。但运行指标以寻找较大的差异是值得的。

有一次我调查了几十个项目,其中一个项目的最大复杂度值约为 6,000,而其他所有项目的最大复杂度值约为 100 或更低。这就像棒球棒一样击中了我的头。显然,该项目正在进行一些不寻常的,并且可能是坏事。

于 2008-11-22T15:22:13.907 回答
1

我们是程序员。我们喜欢数字。

另外,您要做什么,而不是描述代码库的大小,因为“代码行数无关紧要”?

举个愚蠢的例子,150 行代码库和 1.5 亿行代码库之间肯定存在差异。这不是一个很难得到的数字。

于 2008-10-12T21:40:02.070 回答