我建议您熟悉Denis Isaev jirfag的报告的抄写本 “ Linter in Go。如何烹饪它们”。
在Go 50+ linter中:它们的利润是什么,以及如何有效地将其纳入开发过程? 该报告对于尚未使用linter的人和已经使用linter的人都将是有用的:我将揭示鲜为人知的使用linter的技巧和实践。
谁在乎,请在猫下。
你好 我叫Dennis Isaev。 我们将讨论如何在Go中烹饪短绒。 对于尚未使用短绒棉的初学者和专业人士,该报告都将很有趣。 我将告诉您一些鲜为人知的技巧。

关于我的一些事。 我是开源Golangci-lint项目的作者。 在mail.ru工作。 现在,我在Yandex.Taxi的后端担任TeamLead。 我的报告基于数百名Golangci-lint用户的经验。 关于他们如何使用短绒棉,他们遇到了什么困难以及在mail.ru和Yandex中实施Go短绒棉的经验。

我们将在报告中介绍5个关键问题。

我经常看到,短绒根本不使用。 毫无例外地向所有项目中使用lint的人举手。 不是全部。

让我们谈谈为什么不使用它们。 大多数时候,当我问为什么你们不使用时,他们说棉绒会干扰我们。 它们只会减慢发展速度。 他们没有什么好。 这部分是正确的。 如果您不知道调音的微妙之处,那么它们确实会造成干扰。 我们稍后再讨论。

另外,人们经常认为短毛绒只能发现很小的东西,一些风格上的东西,一些非关键的错误,实际上这是不值得的。 浪费时间更容易吗?

立即反例。 使用go vet发现了Docker中的一个错误。 忘记调用取消功能。 因此,后台goroutine可能不会结束。

Etcd项目中的错误。 很酷的批评家linter发现strings.HasPrefix参数很混乱。 意味着检查不安全的HTTP协议将不起作用。

在Go本身中,错误是将i元素与第i个元素进行比较,尽管应该将其与第j个元素进行比较。 也被短绒棉发现。

linters发现的大型开源项目中的三个示例。 有人会问:好吧,那又如何呢? 他发现了一些严重的错误。 对于一个严重的错误,它会发现100个非严重或误报。 我可以提供我的经验统计数据:通常,在linters报告的所有问题中,我约有80%:某些样式问题,例如,未使用变量,等等,其中15%是真实错误,约5%是误报。

现在让我们讨论为什么需要短绒。 最重要的好处是,短绒可以节省时间。 时间就是金钱。 您越早发现错误,对您的公司而言便越便宜。 幻灯片上是一张图表,其中列出了根据发现错误的阶段来修复错误的大约成本。 因此,从开发到生产的每个阶段,成本都会增加三倍。 尽早发现错误,最好在IDE中发现,并节省公司资金。

通常在CodeReview上,开发人员会报告短毛绒可能发现的一些问题。 他们为什么这么做是无法理解的。 首先,代码的创建者需要等待它通过CodeReview。 其次,检查员本人需要花费时间查找任何机械问题。 他可以相信Linter。 当我注意到这一点时,我总是会强迫这样做,并且我们在团队中同意,我们不能考虑对皮棉机可以找到的所有内容进行审查。 我们是否相信所有这些对linter。 此外,如果我们发现一些经常在审查中出现的问题,而且没有毛绒,我们会尝试找到理论上可以抓住它们的毛绒。 因此,我们不会浪费时间进行审查。

Linters使我们能够以某种方式保证项目中的代码质量并使其具有可预测的质量。 例如,在这种情况下,这些是未使用的函数参数。

Linters允许您尽早发现关键错误,从而节省CodeReview时间。 同时,保证项目代码的质量。

Go拥有50多个短绒,但最受欢迎的是4短绒。这些是幻灯片上的。 仅仅因为它们很酷就可以使用它们。 其余通常不想处理。 现在,我想通过例子说明那里有什么样的短绒。 我想演示25个linter示例。 现在,这可能是该报告中最重要的事情。

让我们从检查格式化程序的格式化程序开始。 Gofmt本质上不是短毛。 但是我们可以认为它是短毛猫。 他知道如何告诉我们换行不足,在多余的地方。 通常,这是检查和维护代码格式的标准。

Gofmt还具有一个鲜为人知的选项-s,它使您可以简化表达式。

Goimports包含gofmt包含的所有内容,但除此之外,它仍然知道如何重新排序导入,删除和添加必要的导入。

Unindent是如此出色,可以降低代码嵌套的水平。 在这种情况下,他告诉我们,如果我们将两个if组合为一个,则嵌套级别将下降1。

让我们考虑linters检查代码的复杂性。 其中最酷的是gocyclo。 他是最无聊的。 许多人讨厌他。 他检查代码的循环复杂度,并在函数的复杂度超过某个阈值时发誓。 阈值是可配置的。 如果简化,则圈复杂度就是代码中if的数量。 在这里他太大了,短绒毛发誓。

Nakedret就是这样一个小子,可以说您使用了不带值的return,同时又在一个过长的函数中使用了它。 根据官方指南,不建议您退回。

有一组lint测试样式。 例如,gochecknoglobals检查您是否未使用全局变量。 当然,不需要使用它们。

Golint发誓使用相同的apiUrl变量。 说网址应使用大写字母。 自此缩写。

Gochecknoinits确保您没有使用init函数。 由于某些原因,不应使用初始化函数。

Gosimple酷棉短绒。 staticheck或megacheck的一部分。 内部本身包含大量模式以简化代码。 在这种情况下,您可能会注意到不需要strings.HasPrefix,因为strings.TrimPrefix已包含必要的检查,因此可以删除if。

Goconst检查您的代码中没有重复的字符串文字,这些文字可以被提取为常量。 这些重复的数目是可配置的。 在这种情况下,两个。

拼写错误的linter,它检查注释中的代码是否没有错别字。 在这种情况下,幻灯片在注释文本中包含其他单词的错字。 您可以自定义英语的方言:美国,英国。

取消转换短绒,检查您是否进行了不必要的转换。 在这种情况下,变量已经是字符串类型。 转换没有任何意义。

现在,让我们看一下检查未使用代码的短毛猫。 首先是varcheck。 它检查未使用的变量。

未使用的可以在结构的未使用的区域发誓。

死码告诉我们是否不使用类型。

或未使用该功能。

Unparam可以报告何时在函数主体本身中不使用函数参数。

当代码中不再使用变量更改时,Ineffassign报告。 这是某种重构的结果。 他们忘了清理东西或虫子。 在此示例中,计数增加。 此外,不再使用它。 这与错误非常相似。

有一组棉绒测试性能。 例如,maligned告诉我们可以通过对字段进行重新排序来压缩给定的testStruck结构。 此外,如果将其作为golangci-lint的一部分运行,则它具有一个选项,可让您立即打印所需的字段顺序,以免自己挑选它们。

有这么酷的评论家短毛猫。 他里面有很多支票。 其中之一是巨大的Param。 她知道如何向我们报告有关复制大量数据结构的信息。 在这种情况下,HeavyStruct是按值复制的,我们只需要将其作为指针传递即可。

Prealloc可以在代码中找到可以预部署切片的位置。 他找到了它,以便他搜索我们在切片上进行恒定每小时迭代的位置。 并在其中添加事务。 在这种情况下,您可以为ss片的长度预分配ret变量,从而节省内存和CPU。

最后,发现错误的短毛猫。 Scopelint可能发现初学者最常犯的错误是通过引用捕获循环的范围变量。 在这种情况下,arg循环变量通过引用捕获。 在下一次迭代中,已经有不同的含义。

静态检查 它曾经被称为megacheck。 现在,它已被重命名。 因此,社区中有些混乱。 Staticcheck可以找到大量不同的错误。 这真是一件很酷的事情。 像兽医。 幻灯片上的其中之一是比赛。 当然,我们需要在进入goroutine之前增加sync.WaitGroup。

兽医发现大部分是错误。 在这种情况下,将比较变量i,以使结果始终为真。 因此,显然存在一个错误。 通常,您应该始终使用go vet。

Gosec代表安全性。 在Go中查找潜在的安全问题。 在这种情况下,用户数据可能会到达arg。 因此,它可以渗透rm shell命令。 例如,这里可能有运行中的外壳。 我注意到安全防范通常会产生误报。 因此,有时我将其关闭。

Errchek找到了我们忘记错误检查的地方。 良好,安全的编程风格无处不在,以检查所有错误。

应当注意两个短绒毛:staticcheck和go-critic。 因为其中每个都包含几十个(如果不是数百个)支票。 因此,请务必尝试一下。

现在,我们研究了25个皮棉的例子。 我还说过,Go中有超过50个短绒。 使用哪个? 我建议您尽量使用所有东西。 只要包括所有的短毛绒就可以了。 然后花一个小时,然后一次将其关闭。 关闭那些对您来说无关紧要的文件。 例如,它找到您根本不感兴趣的一些性能优化。 您将花费一个小时为自己创建一个自己的linter列表,然后您可以继续使用它。

幻灯片链接上提供了所有短绒的完整目录。

让我们谈谈如何运行linter。 有时,lint使用此类makefile启动。 问题是它很慢。 全部按顺序执行。

我们可以通过xargs -P并行执行。 还有一个问题。 首先,这些只是4个短毛绒。 并且已经有10行代码。 如果我们打开20个短绒,会发生什么情况。 其次,坦率地说,这种并行化不是最佳的。

Gometalinter进行了救援。 Gometalinter是这样一个linter的聚合器,以至于它可以直接在几个命令中运行。 在幻灯片上,启动同一短毛绒的命令与上一张幻灯片类似。 但是它们不需要独立安装,也不需要通过并行启动进行萨满化。 Gometalinter已经在并行化所有东西。 但是他有一个基本问题。 它作为一个单独的过程启动每个linter。 分叉他。 如果再加上这样的知识,即每个内部的linter花费80%的时间来解析代码,而只花费20%的时间用于分析本身,那么事实就是我们浪费了80%的工作。 并且不要重复使用数据。 我们可以将程序解析1次,然后喂入50个短绒。

幸运的是,有golangci-lint可以做到这一点。 他解析了一次。 键入一次。 在其上运行其他分析器。 因此,它的运行速度更快。 幻灯片上的类似启动命令。

您可以在我的一个项目中看到该图形3万行代码。 一个小项目,只有4个linter。 您会发现有时在顺序发射之间以及gometalinter和golangci-lint之间的工作速度存在巨大差异。 如果这些短绒毛不是4,而是20,则差异会更大。

关于gometalinter的重要说明。 从4月7日起,gometalinter项目的作者宣布已弃用。 该存储库已存档,建议每个人都改用golangci-lint,因为它速度更快,所以在那里有更多东西。 例如,对go模块的支持等等。

除了性能和Go模块外,golangci-lint还具有诸如YAML配置之类的包子,以某种方式跳过警告,排除警告等功能。

使用golangci-lint.yaml文件配置Golangci-lint。 幻灯片上的链接上有此文件的示例,其中包含所有选项的说明。 考虑一下例如linters-settings部分。 在本节中,我们将进行goimports配置。 它具有如此罕见的local-prefixs选项。 在其中,您可以指定当前项目的路径。 在这种情况下,例如github.com/local/repo。

当goimports在github.com/local/repo中看到本地导入时,将确保它们在单独的部分中。

这样它们才是最后。 使它们与所有外部进口产品分开。 这使得在视觉上区分外部导入和内部导入更加容易。 如果他注意到事实并非如此,那么他会发誓。

而且,如果您还使用golangci-lint run --fix选项,那么golangci-lint会自动为您修复它并重新分类导入。

让我们谈谈术语golangci-lint中的短绒。 短毛猫分为快和慢。 快速的称为快速,在帮助中带有快速标记。 它们的不同之处在于,快速更新需要程序的有限表示,例如AST树和某些类型信息。 尽管铜短绒还额外需要程序提交SSA,并且重复使用缓存较少。 只有六个慢速短毛猫。 它们被标记在幻灯片上。 在某些情况下,仅运行快速lint是有意义的。

您可能会注意到速度上的差异。 在快速启动和慢速之间,这是巨大的三倍。 实际上,golangci-lint run --fast仅启动快速的lint。

关于构建缓存。 有诸如构建缓存之类的东西。 这是在加载类型时编译程序时Go二进制文件构建的缓存,以便下次此编译速度更快。 linters会重复使用同一缓存,以解析用于构造类型信息的程序。 您可能会注意到,如果您清除了缓存,则第一次重新开始会很长。 而下一个将快3倍。 请注意您的项目上第一次启动Linter。 它将永远慢得多。

在这里,您可以得出结论,在CI启动之间重用CI有意义。 您不仅会加快linter的速度,还将加快运行测试的速度,只是进行编译,也许还有其他事情。 我建议大家。

我不能谈论围棋分析。 这是Go 1.12之后出现的新框架。 它以这样的方式统一了接口:linter变得易于编写,linter易于使用,运行。 审核开始1.12完全切换到分析。 显然,这就是未来。 它将极大地改变整个围棋生态系统。 但是就目前而言,谈论它通常还为时过早。 那么接下来会发生什么呢? 因为我只看到少量的lint进行了分析,但几乎没有现有的lint切换到它。

如果您在本部分中如何运行短绒棉布上作了简单的总结,那么我建议大家使用golangci-lint。 您将方便快捷地运行linters。 您不需要使用其他指令,命令进行萨满祭司。

让我们讨论一下如何在项目中实施linter。 我认为所有尝试引入短绒棉的人都面临这样的问题。 在这里,您有一个包含历史记录的一百万行代码的项目。 您说服了TeamLead实施Linter。 启动并查看一百万条消息。 了解您几个星期都没有时间坐下来解决所有问题。 怎么办 您可以放弃并放弃一切。 或者,您可以提出一些建议。

首先,最简单的选择是,您可以尝试使用golangci-lint.yaml配置定期在文本上排除短绒棉的一些注释。 如果您看到注释中有短毛绒咒骂,但您通常不关心这些注释,则可以添加例外。

可以排除的方式。 例如,您有一个第三方目录,而您的代码不存在。 您不需要检查它。 可以通过文件名排除。

如果您不想排除整个文件,则可以在函数之前使用nolint排除该函数。 对于nolint,您可以通过冒号指定作为例外的linters列表。 或不指定,则所有短毛绒将被忽略。

什么时候使用nolint? 例如,我使用了nolint:deepguard,它可以捕获导入,即 无法使用导入。 我震惊了logrus库的导入,以免意外使用它代替我想要的记录器。 但是在记录器本身中,我使用logrus。 因此,我只需要在项目中的一个位置即可从导入中仅制作一个文件。 我用nolint标记它。

假设您已完成所有操作,添加排除,添加了nolint词缀。 我们看到仍然有数千条消息。 修复几天。 有一个很酷的技巧。 让我们来看一个例子。 有一个main.go文件,很久以前在其中添加了第5行,而今天才添加了第6行。 我们该怎么办?

我们可以使用revgrep。 Revgrep允许我们指定git修订版,然后需要查找错误。 也就是说,仅在给定修订后才向linter留言。 如果在原点母版之后更改了第六行,则只会对其进行修复。 , 5 . . . . golangci-lint . . , . git hash commit. . . hash commit tag revgrep CI. . . . mail.ru, .

revgrep golangci-lint. --new-from-rev --new. .

. --new. 20 , . . . . . ? --new-from , . .

. golangci-lint . . . , .

我们讨论了在任何项目中引入linter的问题。现在让我们谈谈工作的便利性。首先,您需要实现CI的可重复性。在CI中添加短绒后,就需要它稳定。永远不要去得到。因为它没有版本。Linter随时更改,更新,并且所有CI构建都开始失败。我已经看过数十遍了。始终使用特定版本。用wget更好。她会更快。另外,我不建议对linter使用--- enable-all选项,因为例如,在一天内更新golangci-lint,您添加了5个新的linter,并且所有构建都开始失败。因为您不小心打开了这些Linter。最好明确规定要包括哪些棉短绒。

很酷的事情是预提交钩子。 谁使用预提交钩举手? 很少。 预提交钩子是一个git文件,允许您在提交后执行任意代码。 但是在此提交成功之前。 如果预提交挂钩返回错误,则提交将失败。 通常,在其中嵌入快速测试,静态分析等非常方便。 我建议大家嵌入golangci-lint。 您可以通过Shell脚本手动执行此操作。 通过预提交实用程序是可能的。 如何在幻灯片上进行设置的示例。 使用pip安装预提交,pip是用于安装Python软件包的实用程序。 pip install pre-commit安装配置。 Golangci-lint已经支持与预提交的集成。

--fast选项。 我们回到了她身边。 我建议大家在IDE中使用它。 通常,IDE当然应该与linters集成使用。 为了不使您的IDE冻结,请确保使用--fast选项。

我认为这很明显。 在CI中,需要嵌入短绒。 如果您不嵌入它们,那么将会有一张经典的图片:“现在就锤一下,现在我们有一个发布版本,而不是在此之前。” 逐渐地,您将有越来越多的评论。 您只需停止将短毛绒当作一门课。 因此,严格在CI中。 此外,您可以简单地在CI中安装linters,如果构建失败,我们将进入构建日志,查找其原因。 评论在哪里,在哪一行? 这不是很方便。

有一种更酷的方法。 您可以使linter成为个人,成为审阅者。 他们可以在github.com,gitlab.com上对您发表评论,以获取有关您的Pull请求的错误代码行。 林特可以写道,他发现了问题。 这真是太酷了。 这样可以节省作者时间。 因为您不必进入构建日志。 另外,一个人如果不同意棉绒的话,可以发表评论。 在幻灯片的示例中,这是使用reviewdog实用程序完成的。 实用程序开源。 她有空。 您可以自行安装。

除reviewdog外,还有GolangCI,Code Climate,Hound等项目。 它们使您只需单击一下即可将这些Linter连接到您的opensouce或私有存储库,并在Pull Request中内联注释。 SonarQube仍然很酷。

我还不能提及goreportcard。 该项目使您可以在存储库上生成报告。 他们在那里打成绩,给徽章,写一打清洁棉绒的代码效果如何。 我也建议。

我希望您星期一就可以开始工作,并能够运用我所说的内容。 这是您可以申请的摘要。 首先安装golangci-lint。 打开那里的所有短毛猫。 然后花1个小时。 在这个小时内,关闭所有对您来说似乎有妄想的短绒。 之后,将golangci-lint嵌入到CI中的IDE中,并配置预提交钩子。 在那之后,您可以配置--new-from-rev并指出从当前提交中我们正在寻找错误。 并且所有先前的错误将在以后分别修复。 之后,可以选择配置reviewdog,以便它仍然可以在github或gitlab中向您评论。 您将因此极大地提高项目的质量。 高兴整个团队。

谢谢大家的关注。 幻灯片上的我的联系人。
问题:告诉我,您是否拥有可打开访问的现成配置文件,您可以简单地下载和使用它们,以免理解大量的golangci-lint设置? 您的推荐。
答:好主意。 Golangci-lint本身已经拥有自己的golangci-lint.yaml,它可以使用。 您可以将其用作起点。
问题:在有关构建缓存的幻灯片上,请参考模块的缓存。 在缓存中,您可以指定模块的整个缓存。 您可以指定.cache / downloads,那么会有一个很大的差异:400 MB与10 MB。这足以简单地提取模块。 但这仅在使用模块的情况下。
问题:您是否还将支持go模块或dep或合而为一?
答:不需要任何支持。 现在有一个go包库。 她从事加载源代码。 它同时支持模块和非模块。 她不会,但会删除对非模块的支持。
问题:您打算制作各种插件以不仅与Travis集成,而且与其他自动化服务器集成吗?
答:golangci-lint不进行任何集成。 要在CI中运行,只需调用golangci-lint --run。
问题:例如在Jenkins中,为了解析某些报告,我们保存了一个html文件。
答:有输出格式junit,csv,json xml。 所有这些都已经可以解析。
问:我们之前使用过gometalinter,但速度很慢。 然后我们切换到了一个叫做revive的linter。 你根本没有提到他。 就我而言,我根本不是该主题的专家。 我不知道您讲的是您的短毛猫。 您能否在最后写一句话,比如说您的皮棉的优点或复兴的优点。
答:复兴是改写的go。 这只是短毛绒之一。 有设置。 他还添加了一些皮棉,一些支票。 Golangci-lint本身在30至50短绒之间。 这是一个复兴的半绒毛。 Revive很酷,因为它需要刺针并使其平行。 恢复工作的速度比棉垫要快。 但这只是一头短毛猫。 复兴可以成为golangci-lint的一部分。
问题:您在回顾有关gocritic的短毛绒时发现了一张幻灯片:hugeParam,建议通过指针转移粗体结构。 但这将导致以下事实:所有这些结构将仅在HEAP中分配。 难道这不会带来比优势更大的问题吗? 例如,如果这样的结构被大量传输。
答:我完全同意。 您不应该使用这些警告并且不要仅仅遵循它们。 就像过早的优化一样,它可能会损害项目。 如果我没有完成关键任务,通常我通常会关闭此类短毛绒。 我在哪里发现了探查器的弱点。
问题:我来自Yandex。 我们在大型仓库上使用您的短绒。 我们注意到他已经开始非常快地处理大型存储库。 在短短的几天内,他们编写了一个简单的实用程序,该程序包可以通过go包查找自从向导引入分支以来发生更改的包以及依赖于它们的包。 只对它们运行短绒棉。 棉绒检查加快了几倍。
答:也许您将创建一个idssue,附加一个脚本,很可能我会将所有这些都嵌入golangci-lint中。
问题:是否已为评论计划了严重性级别,以便可以将一些级别包括在报告中,但不会伪造CI流程? 例如,通过完成代码。
答案:很多人问,我将立即说,困难在于这些严格的水平支持了所有人,那里30头短绒中有3或4头。钢铁怎么办? 不清楚。 有必要手动分析他们的评论,以某种方式对其进行标记。 处理误报。 通常这是大量的工作。 我不确定什么时候会做什么。 还有其他方法可以实现相同的目标。
问题:中心上有文章 更准确地说,一系列有关C ++ linter的文章。 该公司正在发展这项业务。 他们从中赚钱。 实际上,他们的工作,或者更确切地说,是该系列出版物,不再针对开发人员,而是更多地针对管理开发人员。 这本质上是纯代码,风格化。 这是我们的任务,也是领导者,团队领导者的任务。 您是否打算在此处使用大量此类资源来推广这种媒体,以便人们阅读并将其介绍给他们的团队? 而且我们不是从下面撞倒的。
答:我有一个计划。 谢谢你的建议。 我本来打算写一篇类似这篇演讲的综合文章,但全年会有更详尽的报道。 也许我会用俄语和英语写。