两人在屏幕上查看软件代码

什么是代码质量?

代码质量及其改进方法

代码质量指的是代码除了能够简单运行并执行其预期功能之外的健壮性。高质量的代码以其高效性、可维护性、可读性和可重用性为特征,而低质量的代码则脆弱、难以解析,并且随着时间的推移容易堆积技术债务

高编码标准对于软件开发过程,就如同适当的准备工作“清洁工作” 对于商业厨房的运作一样重要。提升代码质量的做法可以在短期内带来更好的功能,但其更重要的好处在于长期内更少的问题、更快的进展和更低的维护成本。

程序员有时很难向不太熟悉软件开发生命周期细节的管理层传达更高质量代码的长期收益。在最优代码的整体效益与业务优先级的即时压力之间取得平衡,通常需要复杂的权衡。尽管如此,一项 2022 年对 39 个专有生产代码库的研究指出(除其他发现外)1 : 

  • 因赶工代码而产生的技术债务浪费了开发者高达 42% 的时间。

  • 低质量代码导致的缺陷数量是高质量代码的 15 倍。

  • 解决低质量代码中的问题平均比解决高质量代码中的问题多花费 124% 的时间。

更高质量的代码提高了理解、重构调试和为代码库添加新功能的容易程度和速度。清晰、一致、编写良好的代码有助于开发团队之间更顺畅的协作,并减少代码变更的复杂性和并发症。它不仅驱动强大的软件质量,还驱动良好的开发者体验用户体验

一段代码是否能编译并在运行时成功执行其目的,并不足以决定其整体质量。编写代码不像完成填字游戏,后者只有一种正确完成任务的方式:对于给定的编码问题,通常有无数种解决方案。功能性仅仅 代表可接受代码的最低要求。高质量代码的价值体现在其对周围上下文的次级影响上。

什么是高代码质量?

高质量的代码是一个相对抽象的概念。一段代码的整体质量,既由一系列离散、客观的代码质量度量(尽管存在许多这样的度量)来定义,也同样由它的编写方式以及它与所处更大代码库的交互方式来定义。

高质量代码的常见特征包括:

  • 可读性:代码可读性对于维护、调试以及跨团队和跨时间的协作至关重要。另一位团队成员能轻松理解您的代码吗?多年后工作的另一位程序员,在您不在场提供上下文的情况下,能否准确解读您的代码?

  • 可维护性:代码复杂度通常与代码可维护性成反比。您的代码是否易于测试,使团队能够高效地评估其安全漏洞和优化机会?它是否足够强大,可以在不破坏核心功能的情况下添加新特性,还是过于脆弱,以至于需要进行重大重构才能调整?优先考虑可维护性代码可能会带来额外的前期麻烦,但能在未来节省大量时间和精力。

  • 效率:编写良好的代码可以降低系统的延迟和资源消耗。例如,精心选择的数据结构可以最小化给定函数所需的 CPU 操作次数。周到的数据缓存策略通过消除冗余的数据库查询和网络请求来减少昂贵的输入/输出 (I/O) 操作,同时及时释放未使用的内存避免了不必要的 RAM 膨胀。

  • 可靠性:更少的缺陷和对代码变更具有鲁棒性的结构意味着更少的故障和停机时间。可靠性对于用户体验和信任,以及公司所依赖的关键系统的运行状况至关重要。

卡尔文大学、阿默斯特学院和哥伦比亚大学的研究人员在 2023 年《哈佛数据科学评论》上撰文,提出了一个用于良好代码的规范性框架:“四个 C”。2

  • 正确性 (Correctness):代码完成了它应该完成的任务。作者强调了这一显而易见包含要点的两个推论:“第一,正确性是良好代码的必要但不充分的度量指标。第二,其他目标支持和促进正确性。”

  • 清晰性 (Clarity):任何阅读和编写代码的人都能明白它意图做什么,并能直观地进行必要的修改。

  • 封装性 (Containment):避免蔓延、冗余和不必要的依赖。适当的封装尤其包括:“使用函数来封装可重用的代码,并将跨文件或项目使用的代码保存在一个模块或包中。”

  • 一致性 (Consistency):代码库应在风格、命名约定、注释、缩进和其他实践上保持内部一致性。

随着现代软件开发日益由诸如 IBM Bob智能体编程助手驱动,封装性一致性对于最大化自动化工具的有效性和准确性尤其有用。尽管如此,像 IBM Bob 这样的下一代平台能够实时识别 AI 重构机会,这可以减少执行那种风格规范所需的工作量。

高质量代码的最佳实践

尽管每种编程语言和用例都有其特定的细微差别和细致的考量因素,但在任何场景下都存在一些通用的高质量代码最佳实践。

一种概念化高代码质量的方法就是简单地将其视为尽可能避免低质量代码标志(本文后面将探讨)的代码。

对于更规范的方法,前面提到的《哈佛数据科学评论》(HDSR) 论文概述了一系列确保代码质量的指南。尽管这些指南表面上是为了满足数据科学家的需求而优化,但其中大多数适用于任何编码领域。

选择好的名称

良好的命名约定对于代码可读性和一致性至关重要。作者建议以下做法:

  • 名称的长度应与其作用域成正比。从一个术语的初始定义到其使用之间的距离(无论是在时间、代码行数还是组织结构上)越大,其名称清晰地传达其角色的作用就越重要。

  • 保留缩写对照表。短变量名甚至单字符变量名有助于精简代码,但也可能让不太熟悉项目的人难以理解。维护某种“术语表”可以缓解这种权衡。

  • 统一使用大小写。同时,应避免出现两个名称仅靠大小写区分的情况。

  • 避免使用不具描述性的名称。这会显著增加代码解读的难度。

  • 选择能自然排序的文件命名约定。例如,可以采用 ISO 8601 日期和时间标准,或者用零填充数字,确保它们都有相同的位数。

清晰一致的命名约定也有助于简化向 AI 编码助手下达提示的过程,并提高其输出的准确性。例如,与其提示智能体检查某些类型的变量或探索某个日期范围内的所有文件(这可能需要你的 AI 智能体从上下文中概率性地推断哪些变量或文件符合条件),不如明确提示智能体探索所有以特定数字开头的文件,或所有具有特定名称的变量。

始终遵循风格指南

除了规定良好的命名约定外,一份全面的风格指南最好还应标准化格式元素,包括空格和缩进的使用、注释和数据类型,以及“编码方言”。在 GitHub 上可以找到针对各种编程语言的经过验证的风格指南,例如这个精选列表中的指南。

风格指南也是 AI 编码助手的宝贵参考,可作为特定任务的上下文,甚至是 AI 智能体系统提示的一部分。

选择一套连贯、精简(但足够)的工具集

利用工具集和库自然是提高代码可重用性和效率的好方法,有助于标准化不同团队的工作流程和输出,并广泛加速代码创建。当代码需要协调与复杂第三方系统的交互或处理重复的“已解决”问题时,工具集尤其有用。

但过度依赖工具集可能会增加不必要的臃肿,引入外部依赖,降低代码的健壮性和可维护性。它们也倾向于抽象化代码的底层逻辑,降低其可读性。HDSR 的作者用简单的语言阐述了理想的平衡:“我们希望工具集尽可能简单,但不能更简单。”

不要重复自己 (DRY)

如果您发现自己频繁地复制、粘贴和修改相同的代码块,那么使用一个将重复代码封装在一处的函数可能更好。该函数的参数可以反映不同实例之间变化的元素。这可以清理代码并简化维护,因为它允许您在一个步骤和一个位置调整该函数的所有实例(而不是单独调整代码块的每个副本)。

执行一致性检查

一致性检查是一种自动化验证,用于检验潜在数据或系统状态是否遵循预定义的逻辑规则和约定,有助于避免和处理代码中未预见的冲突和矛盾。这些自动化测试通常是 CI/CD 流水线(持续集成/持续部署)中的标准关键组件。

这是代码可测试性重要性的典型例证。如果您的代码过于复杂或包含过多紧密耦合的依赖,就很难或不可能设计出能够详尽验证每个函数的单元测试。

实施版本控制

版本控制系统有助于促进跨团队的一致性、质量控制、协调和代码评审流程。在使用 AI 驱动的编码框架(尤其是那些可能自主修改代码库的框架)时,确保您有一种方法可以轻松回滚任何不需要或不利的更改。例如,IBM Bob 会自动将您的工作区文件版本化为检查点,以便根据需要轻松回滚代码更改。

什么是坏代码?

广义上讲,坏代码难以阅读和维护,对变更和新特性很脆弱,效率低下且不可靠。它通常带有不必要的依赖,其中不同模块相互交织,对任何一个模块的更改都需要额外工作以避免破坏另一个模块。它缺乏适当的文档,组织混乱,没有连贯、逻辑清晰的结构——这种情况常被称为“意大利面条式代码”。

坏代码通常不仅是(或主要不是)编码技能差的结果,更是糟糕的激励和糟糕的组织结构的产物:过度激进地优先考虑功能发布而牺牲代码质量,通常会带来更快的上市时间,但也会带来更大的未来复杂性和技术债务。

重要的是要记住,坏代码通常也能运行——至少是暂时能运行。如果不是这样,技术债务就不会堆积,因为那些确实有问题的代码就不得不被处理。因此,Martin Fowler 和 Kent Beck 于 1999 年首次出版(此后多次更新)的开创性著作《重构:改善既有代码的设计》使用了代码坏味道这一术语来描述坏代码。它们通常不是缺陷,本身也不会阻止程序运行,但它们指出了设计弱点和代码质量问题,这些问题可能会减慢开发速度或在未来引发缺陷。

Fowler 和 Beck 提出的需要警惕的代码坏味道示例包括:

  • 过长函数(长方法):包含太多行代码的方法。

  • 过大的类:试图做太多事情、包含太多变量、缺乏内聚性的类。

  • 基本类型偏执:使用基本数据类型而不是专门的小对象。

  • 神秘命名:函数或变量命名不当,掩盖了其实际意图。

  • 数据簇:经常到处一起出现的变量组。

  • 冗余元素:功能过少以至于没有存在必要的类或函数。

  • 过长参数列表:需要太多参数才能正确运行的函数。

  • 霰弹式修改:一个更改需要同时修改许多分散的模块(这基本上与过大的类相反)。

  • 重复代码:在多个地方存在相同或非常相似的代码结构。

完整的代码坏味道列表(包含解释、示例和引用)可在此处找到。它们的存在通常表明需要进行重构。在整个组织范围内深入理解这些问题及其引发的复杂性,有助于在开发团队之间建立对质量标准的共同认知。

AI 学院

面向企业的生成式 AI 的兴起

了解生成式 AI 的历史兴起及其对企业的意义。

衡量代码质量

衡量代码质量应始终包含定性评估和定量评估。虽然像循环复杂度这样的客观指标可能有用,但如果没有适当的上下文,它们也可能产生误导。

例如,您的团队可能编写了一套自动化测试,并且你的代码可能在一系列单元测试中达到了 100% 的代码覆盖率。但如果测试套件缺乏真正验证代码完全按需工作所需的一些有意义的断言,那么这种测试覆盖率带来的虚假信心可能弊大于利。

同样,健全的评审结构应同时包含人工代码评审和 AI 代码评审。像 IBM Bob 这样的现代智能编码工具可以实时执行广泛的静态代码分析和重构,但会从传达开发者特定需求和意图的自定义规则自定义模式中大大受益。人并非无所不能,AI 也并非不会出错,但用一方来支撑另一方是确认所有潜在问题都在适当上下文中得到审查的最可靠方法。

始终记住,代码质量依赖于上下文。假设您团队中的一位程序员编写了一个表达清晰、高效、完美实现的算法或代码块,它干净利落地实现了预期的功能。如果这个问题本可以使用每个人都已经熟悉的标准内置库函数有效解决,那么那段优美的代码实际上就是一个质量问题,因为它增加了不必要的复杂性和思维负担。

作者

Dave Bergmann

Senior Staff Writer, AI Models

IBM Think

相关解决方案
IBM Bob

借助您的 AI 合作伙伴 Bob,加速软件交付,实现安全的意图感知型开发。

深入了解 IBM® Bob
AI 编码解决方案

利用可信的 AI 驱动型工具优化软件开发工作,最大限度地减少编写代码、调试、代码重构或代码补全的时间,从而拓展创新空间。

深入了解 AI 编码解决方案
AI 咨询与服务

通过增加 AI 重塑关键工作流程和运营,最大限度提升体验、实时决策能力和商业价值。

深入了解 AI 咨询服务
采取后续步骤

利用生成式 AI 和高级自动化技术加速创建企业就绪代码。Bob 通过建模增强开发人员技能,简化并自动执行开发与现代化工作。

  1. 了解 IBM Bob
  2. 深入了解 AI 编码解决方案
脚注

1. 《红色代码:代码质量的业务影响——对 39 个专有生产代码库的定量研究》, 《国际技术债务会议论文集》 (通过计算机协会数字图书馆访问),2022 年 8 月 16 日

2. 《培养数据科学家更好的编码实践》 ,《哈佛数据科学评论》,2023 年 7 月 27 日