返回首页

清洁代码指南:什么是清洁代码以及如何编写可维护的代码

这份综合指南通过考察软件工程研究中基于证据的原则,回答了什么是清洁代码以及如何编写可维护的代码。它涵盖了有意义的命名、单一职责函数、注释、测试策略、持续重构和架构原则,以降低维护成本并提高团队生产力。

清洁代码:编写持久的可维护软件
Advertisement 728x90

整洁代码指南:编写可维护的软件

在快速发展的软件开发世界中,编写不仅功能正常而且易于理解和修改的代码,是长期项目成功最重要的预测因素。不可维护的代码(通常称为遗留代码)会产生不断累积的“技术债务”,拖慢功能交付速度,并增加关键错误的风险。本指南提供了一个全面、基于证据的框架,用于回答一个根本问题:什么是整洁代码,以及如何编写可维护的代码,使其经得起时间和团队协作的考验。

你将学到什么

整洁代码是团队中任何人都能轻松阅读、理解和修改的代码。对整洁代码的承诺需要一种有纪律、系统化的命名、函数结构和测试方法。根据行业分析,这可直接将软件维护成本降低高达40%。本指南将核心原则分解为可立即实施的步骤。

不可维护代码的高昂成本

在深入探讨“如何做”之前,理解“为什么”至关重要。软件的总拥有成本主要不是由初始开发决定,而是由维护、错误修复和功能增强决定。Standish Group关于软件项目混乱的一项开创性研究发现,开发工作中很大一部分用于返工。

Google AdInline article slot

行业研究(包括IEEE引用的分析)表明,软件维护可能消耗项目总预算的60%到80%。这种财务负担与代码复杂性和可读性直接相关。例如,2018年发表在《Information and Software Technology》上的一项关于软件可维护性的研究,展示了代码度量(如圈复杂度和代码变更率)与实现新功能所需时间之间的强相关性。

基于这些发现和行业基准,一个合理的结论是:对代码质量采取有纪律的方法——回答什么是整洁代码以及如何编写可维护代码的核心问题——可以将维护成本降低40%或更多。这不仅仅是审美偏好问题,而是一个基本的经济要求。

第一步:基础——有意义的命名

编写整洁代码的第一步是培养严格的命名纪律。正如软件工程权威Robert C. Martin在其开创性著作《Clean Code》中所说,命名是可读性的基石。变量、函数或类的名称应揭示其意图,无需注释。

Google AdInline article slot
  • 意图揭示型名称: 名称应回答重大问题:它为什么存在、它做什么以及如何使用。例如,elapsedTimeInDays优于d
  • 避免误导: 不要使用具有误导性或与编程语言关键字冲突的名称。accountList有问题,除非变量确实是ListaccountGroup是更安全的选择。
  • 一致的词汇: 在整个代码库中为每个概念使用一个词。如果你混用getfetchretrieve,开发者无法直观地知道该期待什么。

⚠️ 注意: 糟糕命名约定的成本不是立竿见影的,而是随时间累积。2022年剑桥大学关于开发者认知的一项研究发现,模糊的变量名显著增加了代码理解所需的脑力劳动,导致在维护期间引入错误的可能性更高。

第二步:单一职责原则——函数的单一职责

整洁代码的一个标志是函数级别的单一职责原则(SRP)。一个函数应该做一件事,做好它,并且只做这一件事。

  • 不断提取: 如果一个函数的名称需要用“和”来描述,那它做得太多了。将其分解。
  • 函数大小: 一个函数很少应超过20行代码。这不是一个随意的规则。人类工作记忆的有限容量意味着任何超过一定长度的函数都会变得难以解析。
  • 单一抽象层次: 一个函数不应混合高层业务逻辑和低层实现细节。例如,名为processOrder的函数不应包含特定SQL查询的代码;它应调用saveOrderToDatabase函数。

分析见解: 2020年发表在《Empirical Software Engineering》上的一项对GitHub上开源项目的分析发现,在圈复杂度(代码路径的度量)超过10的函数中,错误频率和修复时间急剧增加。这为保持函数短小精悍的实践提供了实证支持。

Google AdInline article slot

第三步:用注释管理复杂性

与普遍看法相反,整洁代码的目标不是避免注释,而是使它们大多不必要。整洁代码是自文档化的。注释是必要的恶;它们通常用于弥补糟糕的代码。

  • 好的注释: 法律免责声明、复杂算法(代码本身无法简化时)的解释,以及后果警告(例如,“这以O(n²)时间运行”)。
  • 坏的注释: 重复代码内容的冗余注释、像// 这递增i这样的“噪音”注释,以及未随代码变更而更新的过时注释。

减少注释需求的最有效策略是使代码本身更清晰。如果你发现自己需要注释来解释一段代码,这表明代码应该被重构并提取成一个具有描述性名称的函数。

第四步:安全网——测试

整洁代码是可测试的代码。代码质量与测试之间的关系是共生的;两者缺一不可。编写可维护的代码需要一套全面的自动化测试,开发者在每次更改后都可以自信地运行它们。

  • 测试驱动开发(TDD): 虽然未被普遍采用,但在编写生产代码之前编写失败测试的纪律迫使人们关注整洁、解耦的设计。根据ACM一篇关于软件工程实践的论文,TDD可以将缺陷密度降低40-90%。
  • 测试金字塔: 构建测试套件的经典模型,包括:
    • 单元测试(基础): 快速、大量,并隔离测试单个组件。
    • 集成测试(中间): 较少,测试组件如何交互。
    • 端到端测试(顶部): 最少,从用户角度测试系统。
测试类型 速度 测试数量 主要目标
单元 非常快 最多 确认单个单元的逻辑
集成 确认单元之间的交互
端到端 最少 确认系统整体行为

第五步:持续重构与代码审查

整洁代码不是一次性的成就,而是一个持续的过程。没有持续的警惕,代码会腐烂。重构是在不改变外部行为的情况下,有纪律地重构现有代码以改进其内部结构的过程。

  • 童子军规则: 让代码比你发现时更整洁。如果你看到一个命名不佳的变量或一个过长的函数,花几分钟改进它。
  • 代码审查: 这是标准化标准和及早发现可维护性问题的最有效工具。正式的代码审查过程,尤其是由定义好的检查清单指导的过程,充当“第二双眼睛”和知识共享机制。

根据NIST(美国国家标准与技术研究院)记录的软件质量保证实践,代码审查可以识别系统中60%到80%的缺陷。此外,询问“这段代码可维护且易于理解吗?”的审查与询问“这段代码能工作吗?”同样重要。

第六步:架构的基本原则

最后,考虑可维护性需要思考整体架构。这就是面向对象设计的SOLID原则发挥作用的地方。

  • Single Responsibility Principle(单一职责原则):一个类应该只有一个改变的理由。
  • Open/Closed Principle(开闭原则):软件应对扩展开放,对修改关闭。
  • Liskov Substitution Principle(里氏替换原则):派生类应可替换其基类。
  • Interface Segregation Principle(接口隔离原则):多个特定于客户端的接口优于一个通用接口。
  • Dependency Inversion Principle(依赖倒置原则):依赖抽象,而非具体实现。

这些原则为管理依赖关系(变更的隐藏成本)提供了一个健壮的框架。一个与许多依赖紧密耦合的类难以测试且修改风险高。遵循这些原则可以创建一个模块化、解耦的系统,其中组件可以隔离更改,风险最小。

常见问题

编写整洁代码最重要的技能是什么?

最重要的技能是对其他开发者(包括未来的自己)的共情。它需要纪律来编写主要供人类消费、其次才供机器执行的代码。这体现在命名、格式化和架构原则的一致应用上。

如何在不破坏旧代码的情况下开始重构混乱的代码库?

首先编写一套全面的特征测试。这些测试不测试代码应该做什么,而是测试它实际做什么。这创建了一个基于当前行为的安全网。有了这个网,你可以从小处着手,一次重构一个模块,并频繁运行测试以确保没有改变行为。遗留代码的挑战始终是“测试优先”的挑战。

开发者应该在代码审查上花多少时间?

目标不是特定的时间量,而是特定的彻底程度。IEEE关于代码审查有效性的一项研究表明,最佳审查速度是每小时200到400行代码。这个速度允许审查者检查代码、思考设计并识别潜在问题,而不会感到不知所措。确保审查文化是建设性的,并专注于代码而非开发者,也至关重要。

代码库会过于整洁吗?

在软件架构中,有一个概念叫“过度工程”,这是一种复杂性。你可能为永远不会出现的用例编写了整洁、设计精美的代码。目标不是不惜一切代价追求架构纯洁性,而是建立一个适合目的且易于适应变化的系统。务实至关重要;“整洁”的解决方案必须与业务对交付速度的需求以及实际已知需求相平衡。

如何说服经理让我花时间清理代码?

你必须用商业术语来表述对话。技术债务就是债务。它以更慢的功能开发和更高的错误率的形式产生利息。提出成本效益分析。例如,你可以计算当前因理解混乱代码部分而损失的时间。然后,提议一个专门的“重构冲刺”或一个规则:对于每个新功能,花10-20%的时间清理周围代码。未来的投资回报是更快、更可预测的开发过程。

— Editorial Team

Advertisement 728x90

继续阅读