你是否为写得不好的代码而苦恼?你的代码库中充满了不一致的地方吗?每当你的代码被审查时,你是否会感到焦虑?如果你对这些问题的任何一个回答是 "是",那么静态代码分析可以帮助你。
静态代码分析是在代码执行前对其进行分析的过程。它为开发人员提供了许多优势,集成静态代码分析器可以增强你的开发人员的工作流程。
让我们深入了解一下什么是静态代码分析,为什么你应该在开始时使用它,以及你如何在你的项目中快速设置它。
什么是静态代码分析?
在我们刚才提出的所有问题中,这可能是最容易回答的。顾名思义,静态代码分析是对处于静态或非执行状态的代码进行分析。它相当于另一个开发人员阅读和审查你的代码的自动化,只是计算机提供了额外的效率、速度和一致性,这是人类无法比拟的。
它与测试有什么不同?
你可能会想,"如果我在系统层面上对所有的单元和功能测试写了详细的测试,而且它们都通过了,那么我的代码就是没有错误的,对吗?"是的,它是。恭喜你。但是,无错误的代码不等于好的代码;这里面还有很多东西要做。这就是静态分析大显身手的领域。
所有类型的测试,无论是单元测试、功能测试、集成测试、视觉测试,还是回归测试,都要运行代码,然后将结果与已知的预期状态输出进行比较,看是否一切正常。测试可以确保你的代码按照预期功能运行。它把你的代码当作一个黑盒子,给它输入并验证输出。
另一方面,静态代码分析分析其各个方面,如可读性、一致性、错误处理、类型检查以及与最佳实践的一致性。静态分析主要关注的不是你的代码是否提供了预期的输出,而是代码本身是如何编写的。它是对源代码质量的分析,而不是其功能。
总而言之,测试检查你的代码是否工作,而静态分析检查它是否写得好。测试和静态分析是相互补充的,你最好在你的项目中采用两者的健康组合。
为什么使用静态代码分析?
任何能够读取源代码、解析它并提出改进建议的工具都是静态代码分析器。有很多工具都属于静态代码分析器的范畴,从linters和formatters到漏洞扫描器和PR审查器。让我们来看看为什么你应该在你的工作流程中使用这些工具的主要原因。
深入的代码扫描
询问任何开发人员,他们都会证实代码审查是必不可少的。第二双眼睛可以发现你的代码中的问题,而你可能永远无法发现。他们也很可能提出更好的方法来完成任务。有时,阅读别人的代码可以让审查者了解到一些已经内置于项目中的不明显的有用功能。审查者或被审查者(这可能不是一个真正的词,但我还是要用这个词)在这个过程中都能学到一些东西。
但有什么比一个人审查你的代码更好呢?那就是所有的开源开发者都来审查你的代码。静态分析器是由一个庞大的开源规则库驱动的,这意味着每个为该工具做出贡献的人都间接地审查了你的代码。这使得它很难发现一些细微的bug,而这些bug是几个人的审查员可能会错过的,从而溜走。
人们会犯错误。只有15%的代码库安装了JSHint,一个流行的JavaScript代码审查工具,没有问题通过。这恰恰说明了让一些计算机专家来审查你的代码是多么重要。
例子
考虑一下这个让用户挑选自己喜欢的水果的程序。如果你不选择,"芒果 "就是默认的。
这段代码是有效的。对于所有的输入,除了0 ,就是。如果你不是很彻底,你的测试也会毫无悬念地通过。
事实证明,你不能在这个程序中选择一个苹果,因为0 ,像null 和undefined 是一个错误的值。你应该用null-coalescing操作符(??)来代替,而linter会告诉你的。
护栏和训练轮
每个开发者都以自己的个人风格写出不同的代码。但当许多开发人员一起工作时,他们以一致的方式写代码是很重要的。这就是风格指南的作用。建立一个风格指南是写出一致代码的第一步,在与其他开发者一起工作时,风格指南的执行是非常重要的。
风格指南的执行不是一个手动的壮举。没有人能够指望开发人员记住数百条规则,并根据每条规则检查每一行。为什么不让计算机来做这件事呢?
我曾经工作过的每一种语言都有为其编写的linter。JavaScript有ESLint;Python有Black,而Ruby有RuboCop。这些译码器的工作很简单,就是确保你的代码遵循规定的风格规则。一些提示器,如RuboCop,还强制执行良好的做法,如原子函数和更好的变量名称。这样的提示往往有助于在生产中造成问题之前检测和修复错误。
例子
考虑下面这个JavaScript片段,你从一个列表中打印一个水果的名字。这个列表在整个程序中保持不变。
var fruits = ['Apple', 'Banana', 'Cherry', 'Mango']
console.log(fruits[0])
ESLint,如果这样配置的话,可以确保你尽可能地使用常量,以避免你的代码中出现副作用。这是一个很好的做法,但如果你没有linter的话,就很容易错过。
const fruits = ['Apple', 'Banana', 'Cherry', 'Mango']
console.log(fruits[0])
强制使用const 和let ,这些都是块作用域,而不是var ,这样的程序更容易调试,通常被认为是一种好的做法。
即时发现问题...
开发人员喜欢的另一件事是测试他们的代码,确保它在各种输入下都能正常运行。像测试驱动开发这样的做法强调了测试你写的代码的重要性。但编写测试需要时间和精力。很难衡量每一个可能的输入,并确保你的代码能够支持这些输入。最终,测试变得太多,在较大的代码库中需要花费数小时来完成。
静态代码分析器不存在这个问题。你不需要写测试;你可以导入整个预设库。此外,静态分析器的运行速度快得惊人,因为不涉及代码的执行。事实上,许多分析器与编辑器集成,并在你输入时实时突出代码的问题。
例子
有时,实时的速度实在是太快了。
......同样快地修复它们
大多数静态分析器,特别是linters和formatters,不仅会指出问题,而且还能为你修复大部分问题。像Black for Python和ESLint for JavaScript这样的Linters与IDE集成,然后可以在你保存文件的时候自动修复被编辑的文件。
这是非常方便的,因为现在,你的代码质量提高了,甚至不需要你有意识地去想它。作为开发者,我们被宠坏了,不是吗?
例子
ESLint有一个--fix 标志,可以修复一些常见的问题,如不必要的分号、尾部空格和悬空的逗号。
考虑一下过去几个例子中的相同代码片段。(这里"-"代表一个空格)。
var fruits = [
'Apple',
'Banana',
'Cherry',··
'Mango'
];
用--fix 标志运行ESLint,一会儿你就看到了这个:
const fruits = ['Apple', 'Banana', 'Cherry', 'Mango']
好多了!
材料清单
物料清单一般用于供应链管理,作为任何产品的原材料的成本。软件也需要一个类似的材料清单。
当你建立一个应用程序时,你不可避免地使用由其他开发者建立的框架和工具。反过来,这些框架也会使用其他开发者构建的框架。在你知道之前,设置一个简单的Vue.js应用可以在你的node_modules/ 目录中放入成千上万的包。
这就是我们所处的可怕的现实。包建在包的上面。每个巨人都是站在另一个巨人的肩膀上的。你的应用只有在其最弱的依赖关系中才是强大的。漏洞扫描器是另一套静态分析器,它根据广泛的漏洞和利用数据库检查你的依赖树中的每个依赖。所有有已知漏洞的软件包都会被报告,并可以通过一个命令进行更新。
例子
GitHub用Dependabot提供依赖性扫描。npm ,也用npm audit 命令提供漏洞扫描。Dependabot和npm audit 都提供了自动更新有漏洞的软件包到其补丁版本的能力。
自动处理那些无聊的东西
手工代码审查浪费了大量的时间。做审查的人必须从自己的工作中抽出时间来做审查,浏览代码,并指出所有可以改进的地方,包括逻辑上的,也包括微小的细节,如不正确的格式或与惯例和风格指南的偏差。然后,审查员必须做出所有建议的修改,并重复这一过程。
添加一些研磨机、格式化器和拼写检查器,使整个过程更加精简。你问怎么做到的?首先,预提交钩子将确保代码在被签入VCS之前被正确地加注和格式化。第二,以构建管道或GitHub工作流程为形式的项目级自动化将测试每次提交的代码质量,并突出PR本身的问题。第三,审查员将被释放出来,专注于大局,因为在PR进入人工审查之前,所有的小事情都已经处理好了。
没有多少软件的代码审查可以完全取代人工审查。但是,在人工审查之前进行静态扫描,可以通过减少审查员的工作量,轻松地增加审查员的经验,并通过对小问题的迭代,使开发人员的代码比许多轮人工审查更快、更彻底地得到审查。
什么时候
现在。是的,这是对的。我是说现在。比现在再晚就太晚了。如果我不用说服你,你就已经达到了 "如何 "的第二步。
如何做
设置很容易。既然我们在这里反复讨论ESLint,让我们在一个示例项目中设置它。
制作一个新的项目
为你的项目建立一个新的目录。进入该目录,并在该目录中初始化一个Node.js包。npm init 向导会问你一系列的问题。一旦你完成了,你就有了一个新的Node.js包来工作。
$ mkdir wuphf.com
$ cd wuphf.com
$ npm init
安装ESLint
安装ESLint。这太简单了。
配置ESLint
运行以下命令,调出ESLint向导。
$ ./node_modules/.bin/eslint --init
这个向导会问很多关于你将在项目中如何使用ESLint的问题。请确保选择Airbnb规则集。当设置完成后,在目录中会有一个文件.eslintrc.js 。
这个文件定义了该项目将在Node.js上运行,它将在Airbnb风格指南中定义的规则之上进行构建。由于我们正在编写一个控制台应用程序,我可以自定义规则,并关闭警告的那个。
module.exports = {
env: {
es2021: true,
node: true
},
extends: ['airbnb-base'],
parserOptions: {
ecmaVersion: 12
},
overrides: [
{
files: ['*.js'],
rules: {
'no-console': 'off'
}
}
]
}
把这个文件提交到版本控制中。
这就是你的成果!现在项目中的所有JS文件都将被ESLint持续扫描。我还建议你安装Husky,在每次提交前运行一个lint工作,这样你就永远不会在你的VCS中签入不良代码了。
用DeepSource自动处理一切
DeepSource是一个静态代码分析器,可以发现代码库中的问题,并自动提交PR来修复它们。它甚至可以评估PR中传入的代码,并修复它们。它与GitHub、GitLab和Bitbucket的集成度很高,这很奇妙。
你可以在一个项目中设置DeepSource,只需将一个名为.deepsource.toml 的TOML文件放在项目的根目录下,它就会选中该项目并开始扫描。大多数主要语言都被支持。
结束语
这就是全部。用JavaScript静态分析来静态分析你的代码真的很简单,而且好处很多,没有理由不这样做。
祝你写出更干净、更安全、更可读、更可维护(简单地说,就是更好)的代码,我们在下一篇文章中再见。