如果代码只是一次性的,那么代码质量可能不会成为议题。然而,在现实世界中,我们需要经常维护代码,通常是由一个团队来维护。高质量的代码可以降低团队维护代码的成本,从长远来看,这能够为公司节省成本。
与高质量代码相对的是糟糕的代码,在我看来,程序员编写糟糕代码的原因有两种:
- 开发人员的编码能力不足,缺乏编写高质量代码的知识和技能
- 业务开发压力较大,开发者图快写烂代码将就,承诺后续再改
基于上面两个理由,提高代码质量的思路是:
- 提升团队成员编写高质量代码的能力
- 约束团队成员编写高质量代码
提升团队成员编写高质量代码的能力
在聊高质量代码之前,先看看一段烂代码(本示例摘抄自《代码大全》):
js
function handleStuff(inputRec, crntQtr, empRec, estimRevenue, ytdRevenue, screenX, screenY, newColor, prevColor, status, expenseType) {
for (let i = 0; i < 100; i++) {
inputRec.revenue[i] = 0;
inputRec.expense[i] = corpExpense[crntQtr][i];
}
UpdateCorpCache(empRec);
estimRevenue = ytdRevenue * 4.0 / crntQtr;
newColor = prevColor;
status.value = SUCCESS;
if (expenseType === 1) {
for (let i = 0; i < 12; i++) {
profit[i] = revenue[i] - expense.type1[i];
}
} else if (expenseType === 2) {
profit[i] = revenue[i] - expense.type2[i];
} else if (expenseType === 3) {
profit[i] = revenue[i] - expense.type3[i];
}
}
这段代码有不少问题:
- 1.函数名起得很模糊,handleStuff一点都没有告诉你这个函数究竟是做什么的。
- 2.该函数没有注释说明。
- 3.输入的变量inputRec的变量被改变了,如果变量的值是需要被修改的,就不要命名为inputRec。
- 4.该函数读写了全局变量,它从corpExpense中读取数值并将其写入profit。它应该更直接地和其他自程序通信,而不是读写全局变量。
- 5.该函数没有一个单一的目的,它初始化了一些变量,更新了缓存,有做了一些计算,这些事情看不出任何联系。
- 6.该函数用了若干魔法数字:100、4.0、12、2、3等
- 7.该函数参数太多了,函数参数超过3个就应该抽象成一个对象参数。
- etc......
无论是初级还是资深的程序员,都能够识别出上述代码的许多问题。然而,即使是经验丰富的程序员,要完整列出所有问题仍然有一定难度。如果有一份详尽的排查手册或类似文档,这将大大减轻工作量。
此外,即使一些问题是显而易见的,但一些模棱两可的问题可能会引起争议。例如,关于第7个问题,为什么函数的参数必须超过3个才需要抽象成对象参数,而不能是超过5个呢?实际上,无论是3个还是5个都没有问题,关键是要设定一个标准数字,并让团队遵循。
因此,维护一份代码规范在团队内部是非常必要的,它可以解决上述问题排查和代码风格统一的问题。
代码规范
代码规范是一组约定和规则,用于指导编写代码时的风格、格式、命名、缩进、注释和项目结构等方面。代码规范的制定可以提高团队内部代码的可读性、可维护性和一致性,减少潜在错误并提高团队协作效率。
对于代码规范的制定,建议以下几点:
- 按照一定的分类标准划分规范:良好的分类规范可以方便记忆和查找,有助于提高代码编写的效率和一致性。
- 列出好与不好的代码,一目了然:代码规范应当列出示例,明确展示好的编码实践和不良习惯,使团队成员能够清晰理解规范要求。
- 设定"强制"和"建议"级别:为每个规范设置明确的强制性和建议性级别,使团队成员更容易理解和遵守规范。
- 提供培训资料:建议为团队成员提供培训和指导,帮助他们理解和遵守代码规范,从而促进团队内高质量代码的编写和统一认知。
有了明确的代码规范,团队成员可以更有效地编写一致性高的代码,并减少潜在错误。参考像 fex-team JavaScript 代码规范 的规范是一个不错的方法,因为它按照一定标准划分规范、提供示例和建议等,有助于确保团队内代码质量的统一性和提升团队协作效率。
约束团队成员编写高质量代码
人们在编写代码时经常会出现错误,产生不符合质量标准的代码。有时候,出于方便或懒惰,团队成员可能会绕过规范直接编写代码。因此,通过约束团队成员遵循规范编写代码是提高代码质量的另一种方法。
对团队进行约束有两种主要方式:工具和人工。我们先来详细讨论工具约束方式。
代码质量工具
Eslint
ESLint 是一个用于检查 JavaScript 代码规范的工具,可以帮助开发人员发现潜在问题和提高代码风格一致性。ESLint 通过配置文件的方式定义代码规范。
许多大型公司拥有自己的代码规范,如知名的 Airbnb。如果想使用它们的规范,你可以按照以下方式配置 .eslintrc.json
文件:
js
{
"extends" : "airbnb"
}
但 Airbnb 的 Eslint 规范可能并不符合自身团队的风格,或者规范不够完整。此时我们可以根据团队的代码规范,自己编写一套 Eslint 的 extends
。
Prettier
Prettier 是一个 JavaScript 代码格式化工具,专注于代码格式化方面。与 ESLint 不同,Prettier 主要用于规范代码的格式。尽管 Prettier 和 ESLint 在某些功能上有一定重叠,但它们更多的是相互补充的关系。
推荐安装 Visual Studio Code 的 Prettier 插件,这可以在文件保存时自动对代码进行格式化,确保代码风格的一致性和可读性。这样能够帮助团队维持一致的代码风格,提高代码的可维护性和整洁度。
Husky + lint-staged
ESlint 和 Prettier 本身并不具备强制执行规范的能力。开发者可通过直接运行 ESlint 和 Prettier 命令,并提交代码。为解决此类问题,Husky 应运而生。Husky 是用于设置 Git 钩子的工具,允许在 Git 操作之前或之后执行自定义脚本。其中,常用的 Git 钩子是 pre-commit
钩子,用于在提交前进行代码检查。
通过安装 Husky 的 npm 包,在 package.json
中进行如下配置:
js
{
"scripts": {
"prepare": "husky install"
}
}
接下来,在根目录下创建 .husky/pre-commit
文件,这是一个 Shell 脚本,将在每次 git commit 时运行。
建议在 pre-commit
脚本中调用 lint-staged 工具的脚本。使用 lint-staged 工具,可以仅对当前提交的文件执行代码检查,有助于逐步提升项目的代码风格。以下是一个可能的 pre-commit
脚本示例:
sh
npm run lint-staged
Sonar
Sonar 是一个开源的代码质量管理平台,专注于静态代码分析、代码检查、代码度量和代码覆盖率等功能。Sonar 提供了一套工具和平台,帮助开发团队分析代码质量、识别潜在问题并改进代码质量,从而提高软件开发的效率和质量。通过 Sonar,团队可以量化地度量代码质量的变化,有助于对不同规模和类型的工程进行代码质量管理。
Sonar 的主要功能和优势包括:
- 静态代码分析:通过静态代码分析,Sonar 可以发现代码中的潜在问题、代码质量问题和安全漏洞,提供及时的建议和反馈。
- 代码检查:Sonar 提供代码规范检查功能,帮助团队遵循最佳实践和规范,改善代码质量和统一风格。
- 代码度量:通过提供各种代码度量指标和报告,Sonar 帮助团队了解代码质量、复杂性和可维护性等方面的情况。
- 代码覆盖率:Sonar 提供代码覆盖率分析功能,帮助团队评估测试覆盖率,并发现未测试的代码模块。
部署 Sonar 服务有助于团队持续监控和改进代码质量,进而提高软件开发的效率和质量。Sonar 提供了一个全面的解决方案,可以通过量化的方式,帮助团队管理和改善不同规模和类型的工程项目的代码质量。
Code Review
Code Review 是通过同行或团队成员对代码进行检查、讨论和反馈,以提高代码质量的一种方式,也是另一种人工约束代码质量的方法。
Code Review 分为线下 Review 和线上 Review 两种形式。线下 Review 可以是团队在会议室内一起查看代码并讨论。而线上 Review 则是在合并代码到主分支之前,在代码中添加评论和建议。
在进行 Code Review 过程中可能会出现分歧,这时代码规范文档发挥了作用。有了代码规范文档,团队可以将所有分歧统一成一种方式,从而间接提高了效率。通过统一的代码规范,团队可以更容易地解决分歧,确保代码质量和一致性。