Google积累了大量的通用工程实践。这些实践适用于所有编程语言和所有项目。这些文档凝聚了我们多年积累的最佳实践经验。我们相信,开源项目和其他组织也能从中获益,因此我们决定公开分享这些知识。
目前,这个文档集包含以下内容:
- Google代码审查指南,实际上包含两套独立文档:
- 代码审查者指南
- 变更作者指南
术语解释
在这些文档中,我们使用了一些Google内部的专用术语。为了便于外部读者理解,我们在这里特别澄清这些术语:
- CL:代表"changelist(变更列表)",指的是一个自包含的变更,已经提交到版本控制系统或者正在接受代码审查。其他组织通常称之为"change(变更)"、"patch(补丁)"或"pull-request(拉取请求)"。
- LGTM:意思是"Looks Good to Me(看起来不错)"。这是代码审查者批准变更列表时会说的话。
代码审查指南总览
什么是代码审查?
代码审查就像是给你的代码找一个可靠的伙伴。这个伙伴不是代码的作者,而是第三方。他们会仔细检查你的代码,确保一切都符合标准。
在Google,我们把代码审查当作维护代码质量和产品质量的重要手段。这不仅仅是走个形式,更是确保我们软件可靠性的关键环节。
这份文档是Google代码审查流程和政策的权威指南。它详细描述了我们是如何做代码审查的。
这篇文章只是一个概述。如果你想深入了解,指南中还有两个重要文档:
- 如何进行代码审查:专为代码审查者准备的详细指南
- 变更作者指南:专为提交变更的开发者准备的详细指南
代码审查者应该关注什么?
想象一下,你是一位经验丰富的开发者,正在审查别人的代码。你的目标是什么呢?让我们一起来看看:
- 设计:代码设计得好不好?是否适合你们的系统?
- 功能:代码的行为是否符合作者的预期?这种行为对用户来说是否合理?
- 复杂度:代码能不能更简单一些?其他开发者将来能不能轻松理解和使用这段代码?
- 测试:代码有没有正确且设计良好的自动化测试?
- 命名:开发者给变量、类、方法等起的名字清晰吗?
- 注释:注释是否清晰有用?
- 风格:代码是否遵循我们的风格指南?
- 文档:开发者是否更新了相关文档?
想要了解更多细节,请参考如何进行代码审查。
#### 如何选择最佳审查者?
一般来说,你应该选择那些能够在合理时间内回复审查请求的最优秀的审查者。
最佳审查者是这样的人:他们能够对你的代码给出最彻底、最正确的审查。通常来说,这是代码的所有者。这些人可能在OWNERS文件中列出,也可能不在。有时候,你需要请不同的人来审查变更的不同部分。
如果你找到了理想的审查者,但他们暂时不可用,至少要把他们抄送在你的变更中。
#### 面对面审查(以及结对编程)
如果你和某人结对编程,而这个人有资格对这段代码进行良好的代码审查,那么这段代码就被视为已经审查过了。
你也可以进行面对面的代码审查。在这种情况下,审查者会提出问题,而变更的开发者只在被问到时才发言。
---
如何进行代码审查
想象你是一位经验丰富的开发者,负责审查别人的代码。这可不是一件简单的事,需要多年的经验积累才能做好。本章节的页面包含了基于长期经验的最佳实践建议。这些页面合起来构成一个完整的文档,只是被分成多个独立章节。你不必全部阅读,但很多人发现,完整阅读这套指南对自己和团队都很有帮助。
这份指南包含以下章节:
- 代码审查标准
- 代码审查要点
- 审查变更列表的导航
- 代码审查速度
- 编写代码审查评论
- 处理代码审查中的异议
另请参考变更作者指南,它为正在接受审查的开发者提供了详细指导。
代码审查标准
想象一下,你是一位代码审查者,正在评估一个变更请求。这个变更到底能不能通过呢?代码审查的主要目标是确保Google代码库的整体代码健康随着时间推移而不断提升。
为了实现这个目标,我们需要在几个方面保持平衡。
首先,开发者必须能够推进他们的任务。如果我们从来没有向代码库提交改进,那么代码库就不会改进。同时,如果审查者让任何变更都变得非常困难,那么开发者就会失去提交改进的动力。
另一方面,作为审查者,你有责任确保每个变更的质量足够高,不会降低代码库的整体健康程度。这很棘手,因为通常情况下,代码库的健康会随着时间因小的健康下降而逐渐恶化,特别是当团队面临重大时间压力时,他们觉得必须走捷径才能完成目标。
此外,作为审查者,你对正在审查的代码拥有所有权和责任。你希望确保代码库保持一致、可维护,以及我们在代码审查要点中提到的所有其他方面。
因此,我们建立了以下原则,作为代码审查的标准:
一般来说,审查者应该倾向于批准一个变更列表,只要它已经达到这样的状态:它绝对会改善正在开发系统的整体代码健康,即使这个变更并不完美。
这就是所有代码审查指南中的首要原则。
当然,这里也有一些限制。比如,如果一个变更添加了一个审查者不想要的功能,那么审查者当然可以拒绝批准,即使代码设计得很好。
这里的关键点是,没有所谓的"完美"代码——只有更好的代码。审查者不应该要求作者在批准前把变更的每一个小细节都打磨完美。相反,审查者应该平衡让工作继续前进的需求与他们建议的变更重要性。如果你看到一个变更总体上改善了系统的可维护性、可读性和可理解性,即使它不"完美",也不应该因为它不完美而延迟几天或几周。
审查者应该总是觉得可以留下表达"这个可以更好"的评论,但如果这不是很重要,就在前面加上"小瑕疵:"来让作者知道这只是一个可以忽略的抛光点。
注意:本文档中的任何内容都不支持提交明显会降低系统整体代码健康的变更列表。只有在紧急情况时,我们才会这样做。
#### 指导
代码审查可以发挥重要的指导作用。它可以帮助开发者学习一种新语言、框架,或者通用的软件设计原则。留下帮助开发者学习新东西的评论总是可以的。分享知识是随着时间改善系统代码健康的一部分。只要你的评论纯粹是教育性的,而不是解决当前变更的关键问题,就在前面加上"小瑕疵:"或以其他方式表明这不是强制性的。
#### 原则
- 技术事实和数据优先于意见和个人偏好。
- 在风格问题上,风格指南是绝对权威。如果风格指南中没有要求的纯粹风格问题(如空白等),这是个人偏好的问题。风格应该与现有代码保持一致。如果没有之前的风格,接受作者的选择。
- 软件设计方面几乎从不是纯粹的风格问题或仅仅是个人偏好。它们基于底层原则,应该根据这些原则进行权衡,而不是仅仅基于个人意见。有时候有几个有效的选择。如果作者能够通过数据或基于坚实的工程原则证明几种方法同样有效,那么审查者应该接受作者的偏好。否则,选择由标准的软件设计原则决定。
- 如果没有其他规则适用,那么审查者可以要求作者与当前代码库中的代码保持一致,只要这不会降低系统的整体代码健康。
#### 解决冲突
在代码审查冲突中,第一步总是开发者与审查者试图达成共识,基于本文档的内容和变更作者指南以及本文档审查者指南中的其他文档。
如果达成共识变得特别困难,可以帮助开一个面对面的会议或视频会议,而不仅仅是在代码审查评论中试图解决冲突。(如果你这样做,确保把讨论结果作为评论记录在变更列表中,以供未来读者参考。)
如果这还不能解决问题,最常见的解决方式是升级。通常升级路径是扩大团队讨论,让技术主管权衡,或者让代码维护者决定,或者让工程经理帮忙。
不要让变更列表因为作者和审查者无法达成协议而搁置。
#### 代码审查要点
注意:总是要把代码审查标准考虑在内。
##### 设计
审查中最重要的部分是整体设计。各种代码片段之间的交互是否合理?这个变更是否属于你们的代码库,或者属于一个库?它是否很好地集成到你们的系统中?现在是添加这个功能的好时机吗?
##### 功能
这个变更列表做了开发者预期的事吗?开发者预期的事对代码的用户来说是否合理?"用户"通常既包括终端用户(如果变更影响他们),也包括开发者(他们将来会"使用"这段代码)。
通常,我们期望开发者在代码审查之前充分测试他们的变更列表,确保它们能正常工作。然而,作为审查者,你仍然应该考虑边缘情况,寻找并发问题,试着像用户一样思考,确保没有你读代码时发现的bug。
你可以验证变更列表,如果你想这样做——当变更具有用户界面影响时,最重要的是让审查者检查变更的行为,比如UI变更。在代码中很难理解某些变更如何影响用户。对于这样的变更,你可以让开发者演示功能,如果不方便把变更补丁并自己尝试的话。
有时候,特别重要的是在代码审查期间考虑功能,如果变更列表中涉及到某种并行编程,可能导致死锁或竞态条件。这些类型的问题很难通过仅仅运行代码来检测,通常需要开发者(和审查者)仔细思考这些问题,确保没有引入问题。(注意,这也是为什么不使用可能导致竞态条件或死锁的并发模型的原因之一——这可以使做代码审查或理解代码变得非常复杂。)
##### 复杂度
变更列表比它需要的更复杂吗?在变更列表的每个层面检查这个——单个行是否太复杂?函数是否太复杂?类是否太复杂?"太复杂"通常意味着"不能被代码读者快速理解。"它也可以意味着"开发者在试图调用或修改这段代码时很可能引入bug。"
复杂性的一个特定类型是过度工程,开发者使代码比需要的更通用,或者添加了系统目前不需要的功能。审查者应该特别警惕过度工程。鼓励开发者解决他们现在知道需要解决的问题,而不是推测将来可能需要解决的问题。未来的问题应该在它出现在物理宇宙中时解决,那时你可以看出它的实际形状和要求。
##### 测试
要求单元测试、集成测试或端到端测试,根据变更的需要。通常,除非变更正在处理紧急情况,否则测试应该在生产代码的同一变更列表中包含。
确保变更列表中的测试是正确的、合理的和有用的。测试不会自己测试,我们很少为我们的测试写测试——一个人必须确保测试是有效的。
测试会实际失败当代码坏掉时吗?如果代码在它们下面改变,它们会开始产生假阳性吗?每个测试是否做出简单有用的断言?测试是否被适当地分开到不同的测试方法中?
记住,测试也是必须维护的代码。不要接受测试中的复杂性,因为它们不是主二进制文件的一部分。
##### 命名
开发者为一切东西选择了好的名字吗?一个好的名字足够长以完全传达项目或它做什么,没有长到难以阅读。
##### 注释
开发者写了清晰易懂的英文注释吗?所有注释实际上都是必要的吗?通常,注释是有用的当它们解释为什么某些代码存在时,而不是解释代码在做什么。如果代码不够清楚以解释自己,那么代码应该被简化。有一些例外(正则表达式和复杂算法经常大大受益于解释它们在做什么的注释,例如)但通常注释是为了代码本身无法包含的信息,比如某个决定的推理。
在当前变更列表之前存在的注释也可能是有帮助的。也许有一个TODO(待办事项)可以被移除现在,一个建议不要做出这个变更的注释,等等。
注意,注释不同于类的、模块的或函数的文档,它应该表达代码片段的目的、如何使用它,以及使用时它的行为。
##### 风格
我们在Google为所有主要语言有风格指南,甚至为大多数次要语言。确保变更列表遵循适当的风格指南。
如果你想改进风格指南中没有的某个风格点,在你的评论前加上"小瑕疵:"以让开发者知道你认为这会改善代码但不是强制性的。不要仅仅基于个人风格偏好阻止变更列表被提交。
##### 一致性
如果现有代码与风格指南不一致怎么办?根据我们的代码审查原则,风格指南是绝对权威:如果风格指南中要求的东西,变更列表应该遵循准则。
在一些情况下,风格指南做出推荐而不是声明要求。在这些情况下,根据本地不一致是否太混乱来判断是让新代码与推荐一致还是与周围代码一致。
如果没有其他规则适用,作者应该与现有代码库保持一致。
鼓励作者提交bug并添加TODO来清理现有代码。
##### 文档
如果变更列表改变了用户如何构建、测试、与代码交互或发布代码,检查它是否也更新了相关的文档,包括README、g3doc(g3doc)页面,以及任何生成的参考文档。如果变更列表删除了或弃用了代码,考虑文档是否也应该被删除。如果文档缺失,要求它。
##### 检查每一行
在一般情况下,查看你被分配审查的每一行代码。一些东西如数据文件、生成的代码或大型数据结构,你有时可以略过,但不要略过一个人写的类、函数或代码块,并假设里面的东西是好的。显然,一些代码比其他代码值得更仔细的审查——这是你必须做出的判断调用——但你应该至少确保你理解所有代码在做什么。
如果代码太难阅读,这正在减慢你的审查,让开发者知道这一点,并在他们澄清之前等待他们。 在Google,我们雇用伟大的软件工程师,你就是其中之一。如果代码对你来说不够清楚,很可能其他开发者也不会理解。这也是在帮助未来代码读者理解这段代码,当你要求开发者澄清它时。
如果你理解代码但你觉得你没有资格做某些部分审查,特别是对于复杂问题如隐私、安全、并发、可访问性、国际化等,确保变更列表上有合格的人来审查。
###### 例外情况
如果不让你审查每一行有什么意义?例如,你是一个更大变更的多个审查者之一,可能被要求:
- 只审查某些属于更大变更的文件。
- 只审查变更列表的某些方面,如整体设计、隐私或安全影响等。
在这些情况下,在评论中注明你审查了哪些部分。优先给出LGTM并有评论。
如果你反而希望在确认其他审查者已经审查了变更列表的其他部分后给出LGTM,在评论中明确注明以设定期望。旨在在变更列表达到期望状态后快速响应。
##### 上下文
查看变更列表在更广泛的上下文中通常是有帮助的。通常,代码审查工具只会向你显示正在更改的部分周围的几行代码。有时候你不得不查看整个文件以确保变更实际上是有意义的。例如,你可能看到只添加了四行新代码,但当你查看整个文件时,你看到这些四行在一个现在真的需要被分解成更小方法的50行方法中。
查看变更列表在整个系统中的上下文也很有用。是这个变更列表在改善系统的代码健康,还是它在使整个系统更复杂、测试更少等?不要接受会降低系统代码健康的变更列表。大多数系统通过许多小的变化随着时间积累而变得复杂,所以防止新变化中的即使小复杂性是很重要的。
##### 好的东西
如果你在变更列表中看到好的东西,告诉开发者,尤其是当他们以很好的方式处理了你之前的评论时。代码审查经常只关注错误,但它们应该提供鼓励和欣赏优秀实践。作为指导,有时告诉开发者他们做得好比告诉他们做得不好更有价值。例子:开发者以模范方式清理了一个凌乱的算法,添加了杰出的测试覆盖,或者作为审查者你从变更列表中学到了新东西。只像所有评论一样,包括为什么你喜欢某些东西,进一步鼓励开发者继续这些好实践。
##### 总结
在做代码审查时,你应该确保:
- 代码设计良好。
- 功能对代码的用户来说是好的。
- 任何UI变化都是合理的,看起来不错。
- 任何并行编程都是安全完成的。
- 代码不像它需要的那么复杂。
- 开发者没有实现他们现在不知道需要但将来可能需要的功能。
- 代码有适当的单元测试。
- 测试设计良好。
- 开发者为一切东西使用了清晰的名字。
- 注释清晰有用,大多解释为什么而不是什么。
- 代码适当地记录(通常在g3doc中)。
- 代码符合我们的风格指南。
确保审查你被要求审查的每一行代码,查看上下文,确保你在改善代码健康,并对开发者做的好东西给予赞扬。
#### 审查变更列表的导航
##### 总结
现在你知道了要关注什么,跨多个文件审查的最有效方式是什么?
1. 变更是否有意义?它有好的描述吗?
2. 首先查看变更的最重要部分。是整体设计良好的吗?
3. 以适当的顺序查看变更的其余部分。
##### 第一步:广泛查看变更
查看变更列表描述和变更总体上做什么。这个变更甚至有意义吗?如果这个变更根本就不应该发生,立即回应并解释为什么变更不应该发生。当你以这种方式拒绝变更时,建议开发者应该做什么来替代也很好。这显示了我们尊重彼此作为开发者,即使我们不同意。
例如,你可以说"看起来你在这上面做了很好的工作,谢谢!然而,我们实际上正朝着移除你在这里修改的FooWidget系统的方向发展,所以我们现在不想对它做任何新的修改。你可以考虑转而重构我们的新BarWidget类吗?"
注意审查者不仅拒绝了当前的变更列表并提供了替代建议,而且做到了礼貌。这很重要,因为我们想要显示我们尊重彼此作为开发者,即使我们不同意。
如果你收到几个不应该做的变更代表的变化,你应该考虑重新安排你们的团队开发过程或外部贡献者的发布过程,这样在变更列表被编写之前就有更多沟通。这比在浪费大量工作后才说"不"要好得多。
##### 第二步:检查变更的主要部分
找到变更的"主要"文件或文件。通常,有一个文件有最多的逻辑变化,这就是变更的主要部分。先查看这些主要部分。这有助于为变更的其他较小部分提供上下文,并通常加速代码审查。如果变更太大以至于你无法弄清楚哪些部分是主要的,要求开发者告诉你应该先看什么,或者让他们将变更分成多个变更列表。
如果你在变更的这个部分看到一些主要的设计问题,你应该立即发送这些评论,即使你没有时间审查变更的其余部分。审查其余部分可能是在浪费时间,因为如果设计问题足够重要,很大一部分正在审查的代码可能会消失而不是重要。
有两个主要原因使立即发送这些主要设计评论如此重要:
- 开发者通常在等待审查时立即开始基于变更列表的新工作。如果变更列表中有主要设计问题,他们也会不得不重新处理他们的后续变更列表。你希望在他们做太多额外工作之前抓住他们。
- 主要设计变更需要更长时间才能完成。开发者几乎总是有截止日期;为了在保持代码质量的同时满足这些截止日期,他们需要尽快开始对变更列表的任何重大重新工作。
##### 第三步:以适当顺序查看变更的其余部分
一旦你确认了变更列表作为一个整体没有主要设计问题,试着找出查看文件的逻辑顺序,同时确保你不会错过审查任何文件。通常,在查看主要文件后,最简单的方法是按照代码审查工具向你呈现它们的顺序查看每个文件。有时候,阅读测试在阅读主要代码之前也很有帮助,因为那样你就有了一个关于变更应该做什么的想法。
#### 代码审查速度
##### 为什么代码审查应该快速?
在Google,我们优化的是整个开发者团队产生产品的速度,而不是优化单个开发者写代码的速度。单个开发者的速度很重要,但不如整个团队的速度重要。
当代码审查慢时,会发生以下几件事:
- 整个团队的速度下降了。 是的,单个不快速回复审查的人在其他工作上取得了进展。然而,其他团队成员的功能和bug修复因为每个变更列表等待审查和重新审查而延迟了几天、几周或几个月。
- 开发者开始抗议代码审查过程。 如果审查者只在几天后回复,但每次都请求重大变更,这可能令人沮丧和困难。往往,这是以抱怨审查者"太严格"的形式表达出来的。如果审查者请求同样的重大变更(这些变更确实改善了代码健康),但快速回复每次开发者做出更新,投诉往往会消失。大多数关于代码审查过程的投诉实际上是通过使过程更快来解决的。
- 代码健康可能受到影响。 当审查慢时,有增加的压力让开发者提交不是他们能做到的最好的变更列表。慢审查也阻止了代码清理、重构,以及对现有变更列表的进一步改进。
##### 代码审查应该有多快?
如果你没有在专注任务中,你应该在收到代码审查请求后不久就做代码审查。
响应代码审查请求的最大时间是一个工作日(即第二天早上)。
遵循这些准则意味着典型的变更列表应该在一个工作日内获得多次审查回合(如果需要)。
##### 速度与中断
有一个时候个人速度的考虑优先于团队速度。如果你正在专注任务中,比如写代码,不要中断自己来做代码审查。
研究显示,开发者在被中断后回到顺畅开发流程中需要很长时间。所以中断自己写代码实际上比让另一个开发者等一会儿更昂贵。
相反,在你的工作休息点回复,比如当前编码任务完成后、午餐后、会议后、从休息室回来等。
##### 快速响应
当我们谈论代码审查的速度时,我们关注的是响应时间,而不是变更列表通过整个审查和被提交需要多长时间。整个过程也应该快,理想情况下,但让单个响应快速比让整个过程迅速运行更重要。
即使有时候整个审查过程需要很长时间,审查者快速回复整个过程也显著缓解了开发者可能感受到的"慢"代码审查的挫败感。
如果你太忙而无法在变更列表进来时做完整审查,你仍然可以发送快速响应让开发者知道你什么时候能处理它,建议其他可能更快响应的审查者,或者提供一些初步的广泛评论。(注意:这并不意味着你应该中断编码甚至发送这样的响应——在你的工作合理休息点发送响应。)
审查者应该在审查中花足够的时间以确定他们的"LGTM"意味着代码符合我们的标准。然而,单个响应仍然应该理想地快。
##### 跨时区审查
当处理时区差异时,试着在他们有时间回复你的评论之前回复开发者,在他们工作日结束之前。如果他们已经完成了当天工作,那么试着在他们第二天开始工作之前完成你的审查。
##### LGTM并有评论
为了加快代码审查,在某些情况下审查者应该给出LGTM/批准,即使他们也在变更列表上留下未解决的评论。当至少满足以下选项之一时,可以这样做:
- 审查者确信开发者会适当地处理所有审查者的剩余评论。
- 评论不是必须的。
- 建议是小的,比如排序导入、修复附近的拼写错误、应用建议的修复、移除未使用的依赖等。
审查者应该指定他们打算哪个选项,如果不是其他方式清楚的话。
LGTM并有评论在开发者与审查者在不同时区时特别值得考虑,在这种情况下开发者可能会为了仅仅得到"LGTM"而等待整整一天。
##### 大变更列表
如果你收到一个大到你不确定什么时候能有时间审查的代码审查,你的典型响应应该是要求开发者将变更列表分成几个更小的变更列表,而不是一次性审查一个巨大的变更列表。这通常很有帮助,即使对审查者来说。 如果变更列表不能被分成更小的变更列表,至少在整体设计上写一些评论并发送回给开发者改进。一个审查者的目标应该是总是解锁开发者或让他们能够采取某种进一步行动,而不牺牲代码健康。
##### 时间随时间的代码审查改进
如果你遵循这些准则并对你的代码审查严格,你应该发现整个代码审查过程随着时间变得越来越快。开发者学习了健康代码的要求,并从一开始就发送优秀的变更列表,要求越来越少的审查。审查者学会快速回复并且不在审查过程中添加不必要的延迟。
但不要为了想象中的速度改进而妥协代码审查标准或质量——这实际上不会让任何事情更快,在长期。
##### 紧急情况
也有紧急情况,变更列表必须通过整个审查过程非常快速,在这种情况下质量准则会被放松。然而,请见什么是紧急情况?以了解哪些情况实际符合紧急情况的标准,哪些不是。
#### 编写代码审查评论
##### 总结
- 要友善。
- 解释你的推理。
- 在给出明确指示和仅仅指出问题让开发者决定之间取得平衡。
- 鼓励开发者简化代码或添加代码注释而不是只是解释复杂性给你。
##### 礼貌
一般来说,在审查你正在审查的代码的开发者时保持礼貌和尊重很重要,同时要非常清楚和有帮助。一种方法是确保你总是对代码做评论,而不是对开发者做评论。你并不总是需要遵循这个实践,但你应该在说一些可能否则令人不安或有争议的话时使用它。例如:
错误:"为什么你在这里用线程,当并行明显没有好处?"
正确:"这里的并发模型给系统增加了复杂性,没有我能看到的实际性能好处。因为没有性能好处,最好让这段代码是单线程而不是使用多个线程。"
##### 解释为什么
你会注意到上面的"正确"例子帮助开发者理解你为什么做评论。你并不总是需要在你的审查评论中包含这个信息,但有时在你的意图、你遵循的最佳实践,或你的建议如何改善代码健康周围给出更多解释是合适的。
##### 给予指导
一般来说,修复变更列表是开发者的责任,而不是审查者的。你不需要为开发者设计详细的解决方案或写代码。
这并不意味着审查者应该没有帮助,尽管如此。一般来说,你应该在指出问题和提供直接指导之间取得适当的平衡。指出问题并让开发者做出决定通常帮助开发者学习,并使做代码审查更容易。它也可能导致更好的解决方案,因为开发者比审查者更接近代码。
然而,有时直接指示、建议,甚至代码更有帮助。代码审查的主要目标是获得最好的变更列表。次要目标是通过减少和减少审查来改善开发者的技能,随着时间的推移。
记住,人们通过强化他们做得好的和不那么好的来学习。 如果你在变更列表中看到你喜欢的东西,也评论那些!例子:开发者以模范方式清理了一个凌乱的算法,添加了杰出的测试覆盖,或者作为审查者你从变更列表中学到了新东西。只像所有评论一样,包括为什么你喜欢某些东西,进一步鼓励开发者继续这些好实践。
##### 标记评论严重性
考虑标记你的评论的严重性,将必需的变更与指南或建议区分开。
这里有一些例子:
> Nit:这只是一个小事。从技术上讲你应该做它,但它不会大幅影响事情。
>
> 可选(或考虑):我认为这可能是个好主意,但不是严格要求的。
>
> FYI:我不期望你在这次变更列表中做这个,但你将来思考它可能很有趣。
这让审查意图明确,并帮助作者优先考虑各种评论的重要性。它也帮助避免误解;例如,如果没有评论标签,作者可能将所有评论解释为强制性的,即使有些评论只是为了信息或可选的。
##### 接受解释
如果你问一个开发者解释他们不理解的代码片段,那通常应该导致他们重写代码更清楚。偶尔,添加代码注释也是合适的响应,只要它不只是解释过度复杂的代码。
只在代码审查工具中写的解释对未来代码读者没有帮助。它们只在少数情况下是可接受的,比如当你审查一个你不很熟悉的领域时,开发者解释了正常读者已经知道的东西。
---
变更作者指南
变更列表作者指南
想象一下,你是一位开发者,正在准备提交变更列表。你希望让代码审查过程尽可能顺利、高效。本章节包含了在代码审查中表现良好的最佳实践。这些准则应该帮助你更快地通过审查,并产生更高质量的结果。你不必全部阅读,但它们适用于每个Google开发者,许多人发现完整阅读这套指南对自己很有帮助。
这份指南包含以下章节:
- 编写好的变更列表描述
- 小变更列表
- 如何处理审查者评论
另请参考如何进行代码审查,它为代码审查者提供了详细指导。
小变更列表
#### 为什么写小变更列表?
小的、简单的变更列表是:
- 审查得更快。 审查者更容易为小变更列表找到五分钟几次,而不是为一个大变更列表设置30分钟块。
- 审查得更彻底。 对于大变更,审查者和作者倾向于因大量的详细评论来回而变得沮丧——有时到重要点被遗漏或丢弃的地步。
- 不太可能引入bug。 因为你在做更少的变更,更容易为你和你的审查者合理地关于变更的影响,并看看是否引入了bug。
- 浪费的工作更少如果它们被拒绝。 如果你写一个巨大的变更列表然后你的审查者说整体方向错了,你浪费了很多工作。
- 更容易合并。 在一个大变更列表上工作需要很长时间,所以你会有很多冲突当你合并时,你会经常不得不合并(或更糟)。
- 更容易设计好。 在一个小变更上抛光设计和代码健康比在一个大变更上精细化所有细节容易得多。
- 更少阻塞审查。 发送自包含的变更部分允许你在等待当前变更列表审查时继续编码。
- 更容易回滚。 一个大变更列表更可能触及文件,这些文件在初始变更列表提交和回滚变更列表之间被更新,复杂化回滚(中间变更列表可能也需要回滚)。
注意:审查者有权仅仅因为变更列表太大而拒绝你的变更。 通常他们会感谢你的贡献但要求你以某种方式把它分成几个更小的变更。这需要很多工作来在你已经写了一个大变更列表后拆分它,或需要很多时间争论为什么审查者应该接受你的大变更列表。写小变更列表从一开始就更容易。
#### 小是什么?
一般来说,变更列表的正确大小是一个自包含的变更。这意味着:
- 变更列表做一个最小变更,解决只一个东西。这通常只是一个功能的其中一部分,而不是整个功能一次。一般来说,更好在写得太小的变更列表与写得太大的变更列表之间犯错。和你的审查者一起找出可接受的大小。
- 变更列表应该包括相关测试代码。
- 审查者需要理解变更列表的一切(除了未来开发)在变更列表、变更列表的描述、现有代码库,或你已经审查的一个变更列表中。
- 系统在变更列表被检查后将继续为用户和开发者良好工作。
- 变更列表不够小以至于其含义难以理解。如果你在添加一个新API,你应该在同一个变更列表中包括API的使用,这样审查者可以更好地理解API将如何被使用。这也防止了检查进未使用的API。
没有硬性规则关于"太大"是多大。100行通常是一个合理的变更列表大小,1000行通常太大,但这取决于你的审查者的判断。变更列表跨多少个文件也影响它的"大小"。一个200行的变更在一个文件中可能可以,但如果跨50个文件通常太大。
记住,尽管你已经亲密地与你的代码从你开始写它的那一刻,审查者往往没有上下文。什么对你来说似乎是可接受大小的变更列表可能对你的审查者来说是压倒性的。当在怀疑时,写你认为你需要写的变更列表更小。审查者很少抱怨收到太小的变更列表。
#### 什么时候大变更列表可以?
有一些情况大变更不是那么坏:
- 你通常可以把删除整个文件算作一个变更行,因为它不需要审查者很长时间来审查。
- 有时候一个大变更列表是由一个自动重构工具生成的,你完全信任它,审查者的工作只是验证和说他们真的想要变更。这些变更列表可以更大,虽然上面的警告如合并和测试仍然适用。
#### 高效写小变更列表
如果你写一个小变更列表然后你等到你的审查者批准它再写你的下一个变更列表,你会浪费很多时间。所以你想要找某种方式工作,不会阻塞你在等待审查时。
这可能涉及同时处理多个项目,为审查者安排立即可用,或者以允许你立即继续工作的方式拆分你的变更列表。
#### 拆分变更列表
当开始工作会产生多个变更列表有潜在依赖关系时,在编码前以高层思考如何拆分和组织这些变更列表通常有用。
除了帮助你作为作者管理和组织你的变更列表,这也使你的代码审查者的事情更容易,这反过来使你的代码审查更高效。
这里有一些拆分工作的策略。
##### 彼此堆叠多个变更
一种不阻塞自己的方式是在另一个变更列表上写一个小变更,发送它去审查,然后立即基于第一个变更列表写另一个变更列表。大多数版本控制系统允许你以某种方式这样做。
##### 按文件拆分
另一种拆分变更列表的方式是通过文件分组,这些文件将需要不同的审查者但否则是自包含的变更。
例如:你发送一个变更列表用于协议缓冲区的修改,另一个用于使用那个协议的代码变更。你必须先提交协议变更列表,但它们可以同时被审查。如果这样做,你可能想要通知两组审查者你写的另一个变更列表,这样他们有上下文你的变更。
另一个例子:你发送一个代码变更的变更列表,另一个用于配置或实验使用那个代码;这也更容易回滚,如果必要的话,因为配置/实验文件有时比代码变更更快推送到生产。
##### 水平拆分
考虑创建共享代码或存根来帮助隔离层之间的变更。这不仅有助于加速开发,还鼓励层之间的抽象。
例如:你创建了一个计算器应用,有客户端、API、服务和数据模型层。一个共享的协议签名可以抽象服务和数据模型层彼此。类似地,一个API存根可以分离客户端代码与服务代码的实现,并使它们独立前进。类似想法也可以应用于更精细的功能或类级别抽象。
##### 垂直拆分
正交于水平的、分层的途径,你可以把你的代码分成更小、完整的垂直功能栈。每个这些功能可以是独立的并行实现轨道。这使一些轨道在其他轨道等待审查或反馈时前进。
回到我们的计算器例子从水平拆分。你现在想要支持新的运算符,比如乘法和除法。你可以把这拆分成乘法和除法的单独垂直或子功能,即使它们有一些重叠如共享按钮样式或共享验证逻辑。
##### 水平和垂直拆分
为了更进一步,你可以结合这些方法,并像这样绘制一个实现计划,其中每个单元格是它自己的变更列表。从模型(底部)开始向上工作到客户端:
| 层 | 功能:乘法 | 功能:除法 |
|--------|---------------|---------------|
| 客户端 | 添加按钮 | 添加按钮 |
| API | 添加端点 | 添加端点 |
| 服务 | 实现变换 | 与乘法共享变换逻辑 |
| 模型 | 添加协议定义 | 添加协议定义 |
#### 分离重构
通常最好在单独的变更列表中做重构,而不是与功能变更或bug修复混合。例如,移动和重命名类应该在一个不同的变更列表中,而不是在修复那个类的bug的变更列表中。在每个变更列表中更容易看到变化是什么,当它们是分离的。
小清理如修复本地变量名可以在功能变更或bug修复变更列表中包括。取决于开发者与审查者的判断,一个重构是否足够大以至于会使审查更困难如果包含在你的当前变更列表中。
#### 在同一变更列表中保留相关测试代码
变更列表应该包括相关测试代码。记住这里的小是概念想法,变更列表是专注的,不是对行数的简单功能。
测试对所有Google变更都是必需的。
一个添加或改变逻辑的变更列表应该伴随新或更新的测试新行为。纯重构变更列表(不打算改变行为)也应该被测试覆盖;理想情况下,这些测试已经存在,但如果它们没有,你应该添加它们。
_独立_测试修改可以先在单独的变更列表中,比如重构指南。这包括:
- 用新测试验证预先存在的、已提交代码。
- 确保重要逻辑被测试覆盖。
- 增加信心在后续重构受影响代码之前。例如,如果你想要重构没有测试覆盖的代码,在提交重构变更列表之前提交测试变更列表可以验证测试行为在重构前后不变。
- 重构测试代码(例如引入助手函数)。
- 引入更大的测试框架代码(例如集成测试)。
#### 不要打破构建
如果你有几个相互依赖的变更列表,你需要找某种方式确保整个系统在每个变更列表提交后保持工作。否则你可能会打破所有其他开发者的构建几分钟之间你的变更列表提交(或更糟如果你的后续变更列表提交出问题)。
#### 不能让它足够小?
有时候你会遇到你的变更列表必须大的情况。这很少是真的。练习写小变更列表的作者几乎总是能找到分解功能成一系列小变更的方式。
在写一个大变更列表之前,考虑是否在它前面以一个重构-only变更列表可以铺平道路为一个更干净的实现。与你的队友交谈,看看任何人对如何实现功能作为一系列小变更有什么想法。
如果所有这些选项失败(这应该极度罕见)然后从你的审查者获得提前同意审查一个大变更列表,这样他们为即将到来的东西警告。期望通过整个审查过程花很长时间,为不引入bug而警惕,并为写测试特别勤奋。
#### 如何处理审查者评论
当你发送一个变更列表去审查时,很可能你的审查者会以几个评论回复你的变更列表。这里有一些关于处理审查者评论的有用东西。
##### 不要个人化
代码审查的目标是维护我们代码库的质量和我们的产品。当一个审查者对你的代码提供批评时,把它当作他们帮助你、代码库和Google的尝试,而不是对你或你的能力的个人攻击。
有时候审查者感到沮丧,他们在他们的评论中表达那种沮丧。这不是审查者的好实践,但作为开发者你应该为这个做好准备。问自己,"审查者试图传达什么建设性的东西给我?"然后像那就是他们实际上说的一样操作。
永远不要在愤怒中回复代码审查评论。 这是严重的专业礼仪违规,会永远在代码审查工具中生活。如果你太生气或恼火而不能礼貌回复,那么从你的电脑走开一段时间,或工作其他东西直到你冷静到可以礼貌回复。
一般来说,如果审查者没有以建设性和礼貌的方式提供反馈,向他们解释你在人前或通过私人邮件。向他们解释你在不喜欢什么和你希望他们怎么做不同。 如果他们也以非建设性方式回应这个私人讨论,或它没有产生预期效果,那么根据适当升级到你的经理。
##### 修复代码
如果一个审查者说他们不理解你的代码中的某些东西,你的第一个响应应该是澄清代码本身。如果代码不能澄清,添加一个解释为什么代码在那里的代码注释。如果一个注释似乎是多余的,只有然后你的响应是代码审查工具中的解释。
如果一个审查者没有理解你的代码片段,很可能其他未来代码读者也不会理解。写一个代码审查工具中的响应不帮助未来代码读者,但澄清你的代码或添加代码注释帮助他们。
##### 合作思考
写一个变更列表可以花很多工作。它经常真的令人满意终于发送一个去审查,觉得它完成了,并且相当肯定不需要进一步工作。收到要求变更的评论可能令人沮丧,尤其是如果你不同意它们。
在这样的时刻,花一点时间退一步考虑审查者是否提供帮助代码库的反馈。如果你能回答那个问题,但不同意评论,这是重要的合作地而不是对抗性地或防御性地:
错误:"不,我不会那样做。"
正确:"我选择了X因为[这些利弊]与[这些权衡]。我的理解是使用Y会更糟因为[这些原因]。你是在建议Y更好服务于原始权衡,我们应该不同权衡,还是别的什么?"
记住,礼貌和尊重应该总是首要优先级。如果你不同意审查者,找方式合作:问澄清,讨论利弊,提供为什么你的方法对代码库、用户和/或Google更好的解释。通常你可以在技术事实的基础上与你的审查者达成某种共识。
有时候,你可能知道某些关于用户、代码库或变更列表的东西审查者不知道。修复代码适当,并与你的审查者讨论,包括给他们更多上下文。通常你可以在你自己和审查者之间基于技术事实达成某种共识。
##### 解决冲突
你的第一步在解决冲突中总是尝试与你的审查者达成共识。如果你可以达成共识,很好。如果不能,参见代码审查标准,它给出在这种情况下遵循的原则。
#### 处理代码审查中的异议
有时候一个开发者会对代码审查推回。如果他们不同意你的建议,或他们抱怨你太严格了。
##### 谁是对的?
当一个开发者不同意你的建议,首先花一点时间考虑他们是否正确。往往他们比你更接近代码,所以他们可能真的有某些方面的更好洞察。他们的论点有道理吗?它从代码健康角度有道理吗?如果是,让他们知道他们是对的,让问题掉。
然而,开发者不总是对的。在这种情况下审查者应该进一步解释为什么他们相信他们的建议是正确的。一个好的解释演示了对开发者回复的理解,以及关于为什么请求变更的附加信息。
特别是,当审查者相信他们的建议会改善代码健康时,他们应该继续倡导变更,如果他们相信结果代码质量改善证明请求的额外工作合理。改善代码健康倾向于以小步骤发生。
有时候需要几次解释建议才能真正渗透。确保总是礼貌并让开发者知道你听到他们说什么,只是你不同意。
##### 让开发者沮丧
审查者有时相信开发者会被要求改进而沮丧。开发者有时确实变得沮丧,但它通常短暂,他们后来非常感谢你帮助他们改善代码质量。通常,如果你在评论中礼貌,开发者根本不沮丧,担心只是在审查者的脑海中。沮丧通常更多关于评论的方式而不是审查者对代码质量的坚持。
##### 以后清理
开发者推回的一个常见来源是他们(合理地)想要完成事情。他们不想通过另一个审查回合来得到这个变更列表,所以他们说他们会在后面的变更列表中清理,但现在LGTM这个变更列表。有些开发者非常好于这个,他们立即写一个后续变更列表修复问题。然而,经验显示,一旦开发者在当前变更列表后写原始变更列表,清理很少发生。这不是因为开发者不负责任,而是因为他们有很多工作要做,清理在其他工作的压力下丢失或被遗忘。因此,通常最好坚持开发者现在清理他们的变更列表,在代码在代码库中并"完成"之前。
如果一个变更列表引入新复杂性,它必须在提交前清理,除非它是紧急情况。如果变更列表暴露周围问题但不能现在处理,开发者应该为清理提交bug并分配给自己,这样它不会丢失。他们可以可选地写一个TODO注释在代码中引用提交的bug。
##### 关于严格性的普遍投诉
如果你之前有相当宽松的代码审查然后你切换到严格的审查,一些开发者会大声抱怨。改善速度你的代码审查通常使这些投诉消失。
有时候需要几个月这些投诉才会消失,但最终开发者倾向于看到严格代码审查的价值,因为他们看到什么伟大的代码帮助生成。有时候最响亮的抗议者甚至成为你的最强支持者一旦发生某些事情导致他们真正看到你通过严格添加的价值。
##### 解决冲突
如果你遵循上面所有但仍然遇到冲突在你和开发者之间不能解决,参见代码审查标准,它给出可以帮助解决冲突的原则和原则。
---
紧急情况
有时候有紧急变更列表必须通过整个代码审查过程尽可能快。
什么是紧急情况?
紧急变更列表会是一个小的变更:允许主要发布继续而不是回滚,修复生产中严重影响用户的bug,处理紧迫的法律问题,关闭主要安全漏洞等。
在紧急情况下,我们真的关心整个代码审查过程的速度,而不是只是响应速度。在这种情况下_only_,审查者应该更关心审查的速度和代码的正确性(它是否实际上解决紧急情况?)而不是其他任何东西。也(显然)这样的审查应该优先于所有其他代码审查,当它们出现时。
然而,在紧急情况解决后你应该再次查看紧急变更列表,给它们更彻底的审查。
什么不是紧急情况?
为了清楚,下面的情况_not_是紧急情况:
- 想要本周而不是下周发布(除非有实际的硬截止日期如伙伴协议)。
- 开发者在一个功能上工作了非常长时间,他们真的想要得到变更列表。
- 审查者都在另一个时区,现在是夜晚或他们离开去异地。
- 这是周五的下午结束,它会真的很好在本周结束前得到这个变更列表,因为开发者离开周末。
- 经理说这个审查必须完成今天因为软(不是硬)截止日期。
- 回滚一个导致测试失败或构建破损的变更列表。
等等。
什么是硬截止日期?
硬截止日期是一个如果错过它会发生灾难性的事情的截止日期。例如:
- 提交你的变更列表到一个日期是必要的因为合同义务。
- 你的产品会在市场上完全失败如果不按某个日期发布。
- 一些硬件制造商只每年发货新硬件一次。如果你错过提交代码给他们的截止日期,那可能灾难性,取决于你试图发货什么类型的代码。
推迟发布一周不是灾难性的。错过重要的会议可能灾难性,但通常不是。
大多数截止日期是软截止日期,不是硬截止日期。它们代表对功能按某个时间完成的期望。它们重要,但你不应该牺牲代码健康来满足它们。
如果你有长发布周期(几周),很容易诱惑牺牲代码审查质量来在下一个周期前得到功能。然而,这种模式如果重复,是常见的方式团队构建压倒性的技术债务。如果开发者通常在周期结束时提交"必须进入"的变更列表只有表面审查,那么团队应该修改其过程这样大功能变更在周期早期发生,有足够时间进行良好审查。
---
编写好的变更列表描述
变更列表描述是变更的公开记录,确保它传达:
1. 做了什么变更?这个应该总结主要变更,让读者不用阅读整个变更列表就能了解发生了什么变更。
2. 为什么做这些变更?作为作者你在做这个变更时有什么上下文?有没有你做的决策没有反映在源代码中?等等。
变更列表描述将成为我们版本控制历史的永久部分,可能被数百人多年来阅读。
未来的开发者会基于描述搜索你的变更列表。某人将来可能因为模糊记得它的相关性但没有具体细节而寻找你的变更。如果所有重要信息都在代码中而不是描述中,那会让他们更难定位你的变更列表。
然后,在他们找到变更列表后,他们能否理解为什么做出这个变更?阅读源代码可能揭示软件在做什么但可能不揭示它为什么存在,这会让未来的开发者更难知道他们是否可以移动Chesterton的栅栏。
写得好的变更列表描述会帮助那些未来的工程师——有时候,包括你自己!
第一行
- 正在做什么的简短总结。
- 完整句子,像命令一样写。
- 后面跟着空行。
变更列表描述的第一行应该是_具体_正在做什么的短总结,后面跟着空行。这是在版本控制历史摘要中出现的内容,所以它应该信息丰富到未来的代码搜索者不用阅读你的变更列表或整个描述就能理解你的变更列表实际上_做了什么_或它如何与其他变更列表不同。也就是说,第一行应该独立,让读者能更快地浏览代码历史。
尽量保持你的第一行简短、专注和切中要点。对读者的清晰度和实用性应该是首要关注点。
传统上,变更列表描述的第一行是一个完整句子,像命令一样写(祈使句)。例如,说"删除FizzBuzz RPC并用新系统替换它。"而不是"正在删除FizzBuzz RPC并用新系统替换它。"你不必把描述的其余部分写成祈使句,虽然。
正文要信息丰富
第一行应该是简短、专注的总结,而描述的其余部分应该填补细节并包括读者需要整体理解变更列表的任何补充信息。它可能包括正在解决的问题的简要描述,以及为什么这是最佳方法。如果方法有任何缺点,应该提到它们。如果相关,包括背景信息如bug编号、基准结果,以及指向设计文档的链接。
如果你包含指向外部资源的链接,考虑它们可能由于访问限制或保留政策对未来的读者不可见。在可能的情况下为审查者和未来的读者包含足够上下文来理解变更列表。
即使小变更列表也值得注意细节。把变更列表放在上下文中。
糟糕的变更列表描述
"修复bug"是不充分的变更列表描述。什么bug?你做了什么来修复它?其他类似糟糕的描述包括:
- "修复构建。"
- "添加补丁。"
- "把代码从A移到B。"
- "阶段1。"
- "添加便利函数。"
- "杀死奇怪的URL。"
其中一些是真实的变更列表描述。虽然简短,它们不提供足够有用的信息。
好的变更列表描述
这里有一些好的描述例子。
#### 功能变更
例子:
> RPC: 移除RPC服务器消息空闲列表的大小限制。
>
> 像FizzBuzz这样的服务器有非常大的消息,会受益于重用。使空闲列表更大,并添加一个goroutine(goroutine)随着时间缓慢释放空闲列表条目,这样空闲服务器最终释放所有空闲列表条目。
前几句话描述变更列表实际做了什么。描述的其余部分谈论正在解决的问题,为什么这是一个好的解决方案,以及关于具体实现的更多信息。
#### 重构
例子:
> 用TimeKeeper构造Task来使用其TimeStr和Now方法。
>
> 给Task添加Now方法,这样borglet() getter方法可以被移除(它只被OOMCandidate用来调用borglet的Now方法)。这替换了Borglet上的委托给TimeKeeper的方法。
>
> 允许Task提供Now是朝着消除对Borglet依赖的一步。最终,依赖于从Task获取Now的协作者应该被改变为直接使用TimeKeeper,但这已经是对小步骤重构的适应。
>
> 继续长期目标重构Borglet层次结构。
第一行描述变更列表做了什么以及它如何与过去不同。描述的其余部分谈论具体实现、变更列表的上下文、解决方案不是理想的,以及可能的未来方向。它也解释了_为什么_做出这个变更。
#### 需要一些上下文的小变更列表
例子:
> 为status.py创建Python3构建规则。
>
> 这允许已经在Python3中使用这个的消费者依赖于一个在原始status构建规则旁边的规则,而不是他们自己树中的某个地方。它鼓励新消费者如果他们能的话使用Python3而不是Python2,并显著简化当前正在开发的自动化构建文件重构工具。
第一句描述实际正在做什么。描述的其余部分解释_为什么_做出变更并给审查者很多上下文。
使用标签
标签是手动输入的标签,可以用来分类变更列表。这些可能被工具支持或只是团队约定。
例如:
- "\[标签\]"
- "\[更长的标签\]"
- "\#标签"
- "标签:"
使用标签是可选的。
当添加标签时,考虑它们应该在变更列表描述的正文中还是第一行。限制在第一行使用标签,因为这会模糊内容。
带和不带标签的例子:
``{.good}
// 如果保持简短,第一行中标签是可以的。
[banana] Peel the banana before eating.
// 标签可以在内容中内联。
Peel the #banana before eating.
// 标签是可选的。
Peel the banana before eating.
// 如果保持简短,多个标签是可以接受的。
#banana #apple: Assemble a fruit basket.
// 标签可以在变更列表描述的任何地方。
> Assemble a fruit basket.
>
> #banana #apple
{.bad}
// 太多标签(或太长的标签)会淹没第一行。
//
// 相反,考虑标签是否可以移到描述正文中和/或缩短。
[banana peeler factory factory][apple picking service] Assemble a fruit basket.``
生成的变更列表描述
一些变更列表是由工具生成的。只要可能,它们的描述也应该遵循这里的建议。也就是说,它们的第一行应该简短、专注和独立,变更列表描述正文应该包括帮助审查者和未来的代码搜索者理解每个变更列表效果的信息细节。
在提交变更列表前审查描述
变更列表在审查期间可能经历重大变更。在提交变更列表前审查变更列表描述值得,这样确保描述仍然反映变更列表做了什么。
---
专业术语对照表
| 英文术语 | 中文翻译 | 说明 |
|---------|---------|------|
| CL (Changelist) | 变更列表 | Google内部术语,相当于其他组织的"change"、"patch"或"pull-request" |
| LGTM (Looks Good To Me) | 看起来不错 | 代码审查者批准变更时的标准回复 |
| OWNERS file | OWNERS文件 | 指定代码所有者的配置文件 |
| Nit | 小瑕疵 | 微小的、非强制性的改进建议 |
| Nit: | 小瑕疵: | 前缀标记,表示非强制性改进建议 |
| RPC | RPC | 远程过程调用(Remote Procedure Call) |
| goroutine | goroutine | Go语言中的并发单元(协程) |
| g3doc | g3doc | Google内部文档系统 |
| TODO | TODO | 待办事项标记 |
本文档基于Google工程实践文档翻译,采用CC-BY 3.0许可证。原文版权归Google所有。