高质量的代码库使迭代、协作和维护变得更容易,从而加速长期开发。在Quora,我们非常重视代码库的质量。除了好处之外,维护高质量的代码还伴随着巨大的开销,并牺牲了真正的开发周期。很多人发现实际产生的收入很难抵消这笔开销,人们面临两个选择:要么用低质量的代码提高开发速度,要么牺牲开发速度来保持高质量的代码。对于初创公司来说,他们想要发展得更快,所以他们不得不使用低质量的代码。我们开发了一套工具和流程来提高开发速度,同时保持高质量的代码库。在本文中,我们将介绍一些保证代码质量的方法,以及平衡这两个方面的一些具体案例。维护高质量代码的目标维护高质量代码的主要好处是从长远来看推动开发速度,这是我们关注的重点。编写干净代码的短期成本可以换取长期利益。考虑到这一点,这里有四个我们发现对代码质量非常有帮助的基本原则:Quara的代码质量基本原则1.代码应该易于阅读和理解——现在更多的时间花在阅读代码上,而不是写它。事实上,您应该花更少的时间阅读,即使这意味着花更多的时间编写代码。2.代码的不同部分需要有不同的质量标准——不同的代码行需要有不同的生命周期、范围、被破坏的风险、被破坏的成本和修复成本等。总的来说,这些有不同的效果长期迭代速度,所以强制执行统一的质量标准是不合理的。3.可以减少代码质量的开销——通过使用自动化、更好的工具、更好的流程和培训更好的开发人员,通常可以节省维护高质量成本的开销。4.代码库一致性很重要——保持整个代码库的一致性是非常有价值的,即使这意味着代码的某些部分不能完美。不一致的代码库难以阅读和理解(参见***),难以编写,也难以使用自动化工具进行优化。下面,让我介绍几个我们将这些原则应用到实际开发中的例子。提交后的代码审查我们代码库中的代码更改需要从六个维度进行评估——正确性、隐私、性能、架构、可重用性和风格。阅读代码是代码审查的关键环节,因此代码审查对提高代码可读性起着至关重要的作用。但一个坏处是代码审查也会拖慢开发速度。比如pre-commitcodereview,目前在业界比较普遍,代码在推送之前需要经过review和修改。这样的话,即使每一轮review只需要2天,像这样重复2-3轮也会耽误开发人员一周的时间,这是一种严重的时间浪费。在Quora,我们主要做的是post-submissioncodereview。也就是说,代码先进入生产阶段,然后再找人进行代码审查。为了让大家了解这项工作的规模,以昨天为例,我们48个人总共推送了187次代码推送。提交后审查是一个好主意,因为它可以让开发人员腾出时间去做其他工作。代码审查人员也可以更好地管理自己的时间,可以在方便的时候进行代码审查,而不必匆忙完成工作,以免影响其他人的工作。如果流程合理的话,预计codereview可以在一周内完成,但大多数情况下,一到两天就可以完成。之所以说一周,也是经过深思熟虑的,因为一周足够长,可以让审稿人有弹性,而一周又有点短,因为需要把坏结果的影响降到最低。例如,如果有人随后阅读代码,就会在整个代码库中传播错误代码。其实还有一个原因。许多开发人员每周都会安排他们的个人工作。我们可以进行提交后代码审查的原因是因为我们相信我们的每个开发人员,我们选择最优秀的人,并且我们在他们的代码质量培训/工具上投入了大量资金。这也促使我们更好地测试代码,好的测试可以帮助检测代码的准确性,甚至在任何代码审查工作开始之前。为此,我们使用Phabricator,这是一种强大的、高度可配置的审查工具。我们对其进行了多项改进,使其更适合我们的提交后代码审查工作流程。例如,我们构建了一个命令行工具,可以将代码推送到生产环境并提出代码审查请求。使用这个工具,Phabricator的diff可以继续工作而无需修复这些提交。尽管如此,我们对不同类型的代码问题有不同的代码审查标准。如果我们提前发现一个潜在的bug,问题很大,可能影响很大,那么我们会进行pre-commitreview,而不是正常的post-commitreview。举几个例子:1.涉及用户隐私和匿名的代码。2.涉及核心抽象(abstraction),因为很多其他代码可能依赖于它。3.涉及基础设施的某些部分,其中的一些错误可能导致停机的风险。当然这也取决于开发人员如何决定——如果他们想对某些代码进行预提交审查以从中获得更多想法,这也可以不必使用通常的提交后代码审查,但是这情况比较少见。代码审查任务分配为了做好代码审查,每个更改都应由具有相关经验的人员审查。如果这些代码审查者能够负责“维护”他们审查过的代码,从而获得一定的物质奖励,进而形成长期的交易关系,那就更好了。我们之前实现了一个简单的系统,其中模块和目录级代码“所有权”仅通过在文件开头分配元标记来指定。例如:__reviewer__='vanessa,kornel'如果一个commit要对文件做一些改动,它的reviewer会根据文件(或者结构树[ancestortree])来分析它,并自动将它作为一个审稿人。对于代码审查任务的分配或路由,我们还有一些其他规则。例如,如果没有审阅者,可以将数据科学家作为审阅者自动添加到启动A/B测试的提交中。事实上,我们已经构建了一些工具,可以让我们更轻松地制定更多此类自定义“路由”规则。例如,如果我们想这样做,很容易添加一个规则,可以将新任务的所有提交路由和分配给相应的人员。这样一个智能系统,将所有的commit分配给合适的人员进行代码审查,可以有效减轻开发人员的负担,否则他们需要四处寻找合适的审查人员,并确保大多数审查人员是认真的。检查每个提交。测试测试是我们开发过程中非常重要的一步。我们写了很多单元测试、功能测试和UI测试的代码,涵盖了很大的测试范围。综合测试允许开发人员更快地并行工作,而不必担心破坏现有功能。我们投入了大量时间使测试框架(基于nosetests构建)易于使用和快速应用,减少了编写测试代码的开销。我们还开发了几种工具来自动化测试。我们之前发表了一篇关于“持续集成系统”的文章,描述了在部署任何新代码之前运行所有测试的中央服务器。测试服务器可以并行运行,因此运行所有测试只需不到5分钟。这样的快速运行可以激励人们频繁地编写和执行测试程序。我们有一个名为“test-local”的工具,它只识别与对代码的工作副本执行测试更改相关的测试文件。为了做得更好,我们的测试也需要模块化(这可以进一步帮助我们在测试失败时快速调试)。为确保良好的测试代码达到预期的性能,我们维护了一份描述编写测试代码的最佳实践的共享文档。这些准则在代码审查过程中得到严格执行。和codereview一样,我们对不同类型的变更也有不同的测试标准,对测试的范围有更高的要求,尤其是变更成本高的时候。所有这些系统都允许我们从编写测试代码的过程中获得最大收益,以很少的开销节省大量的开发时间。代码质量指南我们非常热衷于分享代码质量标准指南,它可以帮助我们做很多事情:①作为新开发人员培训的好工具。②与整个团队分享每个人的智慧和最佳实践。③减少开发和代码审查过程的开销。比如,如果我们经过一次讨论就可以得出一些有价值的结论,那么每次codereview讨论代码行是80串还是100串就完全没有意义了。除了针对不同语言的语法风格指南,我们还有一些抽象事物的指导,比如如何写出好的测试用例,或者如何做出更好的模块架构来提高工作效率。这些指南不是一成不变的,而是随着我们对不同过程的更好理解而不断发展。我们还有很多重构工具(一些是开源的,比如codemod,一些是内部开发的),以防我们在更改指南时需要重新“修改”所有历史代码。清理旧代码一个快速成长的团队会尝试许多不同的工具,其中一些有效,一些无效。归根结底,随着时间的推移,任何快速发展的公司都会在其代码库中开发出大量冗余的东西,这些东西可能永远不会被再次使用,而往里面放很多东西只会把它弄得一团糟。清理这些过时的代码可以让代码库运行得更健康,提高开发速度。我们定期组织“清理周”来清理这些冗余代码。在清理周期间,某些团队,有时甚至是整个公司,都致力于清理旧代码。这种定期清理模式减少了在“正常工作”和“清理工作”之间来回切换的成本,并使清理更具社交性和乐趣。代码库的某些部分比其他部分更容易清理。同样,清理代码库的不同部分会对开发速度产生不同的影响。为了更好地利用清理旧代码所花费的时间,我们在成本考虑的基础上优化了相关模块进行清理工作,并衡量清理工作对未来开发速度的影响。Linting我们很可能低估了在一个实例中不遵循上述代码质量准则(例如docstring格式或代码行长度)的成本,但实际上成本确实增加了。同时,记住各种规则并应用起来很烦人,尤其是当规则还在增加的时候。最终结果是许多人选择不遵循这些准则。我们开发了一个名为qlint的内部linter,它可以缓解上面的一些问题。qlint是一个非常智能的linter,可以理解文本结构和AST。它基于flake8和pylint。它旨在使将来更容易添加自定义lint规则。例如,我们的一个做法是Python中任何一个“私有”变量都需要被高亮显示,所以我们在qlint中添加了一个新的规则,它可以检测到此类私有变量中任何不合理的地方。我们还将qlint与许多系统集成,以提供无缝的开发环境,因此人们不必自己盯着lint错误。对于初学者来说,qlint是一种与我们常用的文本编辑器(Vim、Emacs和Sublime)完全集成的工具,并在违反规则时提供视觉反馈(红旗)。它还与我们的推送流程集成在一起,任何人在进行代码推送时都可以(交互式)运行qlint。事实上,如果commit破坏了某个规则,可能会影响部署工作。我们还将我们的风格指南与qlint集成在一起,这样对于每个lint错误,qlint都可以给出相关风格指南的相应规则的超链接。我们的Phabricator也可以使用qlint。这样一来,所有qlint发现的错误都被清晰的标记出来,让他们的codereview工作变得相当轻松。这些都帮助我们以很小的成本提高了代码的一致性和质量。结论如文章所述,Quora非常重视代码。我们务实,已经建立了很多好的系统、工具和流程,可以帮助我们提高和保持长期的开发生产力。今天我们努力保持一个很好的平衡,我们的团队也在不断的成长和发展,所以我们相信未来我们会产生更多的工具和系统。如果你想帮助我们建立这样一个系统,或者想成为我们强大的开发团队的一员,以这种务实而深刻的方式帮助我们提高代码质量,那么欢迎加入我们。
