by kevin
7.
七月 2023 13:25
>
The compiler also doesn’t know anything about how to handle external dependencies, such as third-party JAR files in Java. Often the best we can do without a build system is to download the dependency from the internet, stick it in a lib folder on the hard drive, and configure the compiler to read libraries from that directory. Over time, it’s easy to forget what libraries we put in there, where they came from, and whether they’re still in use. And good luck keeping them up to date as the library maintainers release new versions.(编译器也不知道如何处理外部依赖关系,比如Java中的第三方JAR文件。通常,在没有构建系统的情况下,我们能做的最好的事情就是从网上下载依赖关系,把它放在硬盘上的lib文件夹里,并配置编译器从该目录中读取库。随着时间的推移,我们很容易忘记我们把哪些库放在那里,它们来自哪里,以及它们是否仍在使用。而且,当库的维护者发布新的版本时,要想让它们保持最新的状态,那就得靠运气了。)
by kevin
30.
十一月 2022 18:32
>
能够立即有效地浏览整个代码库意味着很容易找到相关的库来重用和好的例子来复制。 Most of the time, developers won’t notice when indices are out of date. They only care about a small subset of code, and even for that they generally won’t know whether there is more recent code. However, for the cases in which they wrote or reviewed the corresponding change, being out of sync can cause a lot of confusion. It tends not to matter whether the change was a small fix, a refactoring, or a completely new piece of code—developers simply expect a consistent view, such as they experience in their IDE for a small project.(大多数时候,开发人员不会注意到索引何时过期。他们只关心一小部分代码,即便如此,他们通常也不知道是否有更新的代码。但是,对于他们编写或审查相应更改的情况,不同步可能会导致很多混乱。更改是小修复、重构还是全新的代码片段往往并不重要——开发人员只期望一个一致的视图,例如他们在 IDE 中为一个小项目所体验的。)
by kevin
18.
十一月 2022 12:27
>
In all of this thinking, we’re assigning special significance to the trunk branch. But of course, “trunk” in your VCS is only the technology default, and an organization can choose different policies on top of that. Perhaps the default branch has been abandoned and all work actually happens on some custom development branch—other than needing to provide a branch name in more operations, there’s nothing inherently broken in that approach; it’s just nonstandard. There’s an (oft-unspoken) truth when discussing version control: the technology is only one part of it for any given organization; there is almost always an equal amount of policy and usage convention on top of that.(在所有这些想法中,我们为主干分支赋予了特殊的意义。但当然,VCS中的 "主干"只是技术默认,一个组织可以在此基础上选择不同的策略。也许默认的分支已经被放弃了,所有的工作实际上都发生在某个自定义的开发分支上——除了需要在更多操作中提供分支名称之外,这种方法没有任何内在的缺陷;它只是非标准的。在讨论版本控制时,有一个(经常不说的)事实:对于任何特定的组织来说,技术只是其中的一部分;几乎总是有同等数量的策略和使用约定在上面。) There are several deeper ideas and policies implicit in our One-Version Rule; foremost among them: development branches should be minimal, or at best be very short lived. This follows from a lot of published work over the past 20 years, from Agile processes to DORA research results on trunk-based development and even Phoenix Project[^12] lessons on “reducing work-in-progress.” When we include the idea of pending work as akin to a dev branch, this further reinforces that work should be done in small increments against trunk, committed regularly.(在我们的 "一个版本规则"中隐含着几个更深层次的想法和策略;其中最重要的是:开发分支应该是最小的,或者最多只能是很短的时间。这来自于过去20年里发表的大量工作,从敏捷过程到基于主干的开发的DORA研究成果,甚至凤凰计划关于 "减少进行中的工作"的教训。当我们把待完成的工作看作是类似于开发分支的想法时,这就进一步强化了工作应该针对主干,定期提交,以小的增量完成。) Choice leads to costs here. We highly endorse the One-Version Rule presented here: developers within an organization must not have a choice where to commit, or which version of an existing component to depend upon. There are few policies we’re aware of that can have such an impact on the organization: although it might be annoying for individual developers, in the aggregate, the end result is far better.(选择带来了成本。我们高度赞同这里提出的 "单一版本规则":组织内的开发者不能选择提交到哪里,或者选择依赖现有组件的哪个版本。据我们所知,很少有策略能对组织产生如此大的影响:尽管这对个别开发者来说可能很烦人,但从总体上看,最终结果要好得多。)
by kevin
15.
十一月 2022 13:44
>
Our discussion of deprecation begins from the fundamental premise that code is a liability, not an asset. After all, if code were an asset, why should we even bother spending time trying to turn down and remove obsolete systems? Code has costs, some of which are borne in the process of creating a system, but many other costs are borne as a system is maintained across its lifetime. These ongoing costs, such as the operational resources required to keep a system running or the effort to continually update its codebase as surrounding ecosystems evolve, mean that it’s worth evaluating the trade-offs between keeping an aging system running or working to turn it down.(我们对“弃用”的讨论始于这样一个基本前提,即代码是一种负债,而不是一种资产。毕竟,如果代码是一种资产, 我们为什么还要费心去尝试“弃用”它呢? 代码有成本,其中有开发成本,但更多的是维护成本。这些持续的成本, 例如保持系统运行所需的运营资源或紧跟周围生态而不断更新迭代花费的精力,意味着你需要在继续维护老化的系统运行和将其下线之间做一个权衡。) Finally, funding and executing deprecation efforts can be difficult politically; staffing a team and spending time removing obsolete systems costs real money, whereas the costs of doing nothing and letting the system lumber along unattended are not readily observable. It can be difficult to convince the relevant stakeholders that deprecation efforts are worthwhile, particularly if they negatively impact new feature development. Research techniques, such as those described in Chapter 7, can provide concrete evidence that a deprecation is worthwhile.(最后,资助和执行“弃用”工作在行政上可能很困难;为团队配备人员并花时间移除过时的系统会花费大量金钱,而无所作为和让系统在无人看管的情况下缓慢运行的成本不易观察到。很难让相关利益相关者相信“弃用”工作是值得 的,尤其是当它们对新功能开发产生负面影响时。研究技术,例如第七章中描述的那些,可以提供具体的证据证明“弃用”是值得的。) Given the difficulty in deprecating and removing obsolete software systems, it is often easier for users to evolve a system in situ, rather than completely replacing it. Incrementality doesn’t avoid the deprecation process altogether, but it does break it down into smaller, more manageable chunks that can yield incremental benefits. Within Google, we’ve observed that migrating to entirely new systems is extremely expensive, and the costs are frequently underestimated. Incremental deprecation efforts accomplished by in-place refactoring can keep existing systems running while making it easier to deliver value to users.(鉴于“弃用”和删除过时软件系统的难度,用户通常更容易就地改进系统,而不是完全替换它。增量并没有完全避免“弃用”过程,但它确实将其分解为更小、更易于管理的块,这些块可以产生增量收益。在 Google 内部,我们观察到迁移到全新系统的成本非常高,而且成本经常被低估。增量“弃用”工作通过就地重构实现的功能可以保持现有系统运行,同时更容易向用户交付价值。)
by kevin
9.
十一月 2022 13:53
>
Unit tests are limited by the imagination of the engineer writing them. That is, they can only test for anticipated behaviors and inputs. However, issues that users find with a product are mostly unanticipated (otherwise it would be unlikely that they would make it to end users as issues). This fact suggests that different test techniques are needed to test for unanticipated behaviors.(单元测试受到编写它们的工程师想象力的限制。也就是说,他们只能测试预期的行为和输入。然而,用户在产品中发现的问题大多是未预料到的(否则,他们不太可能将其作为问题提交给最终用户)。这一事实表明,需要不同的测试技术来测试非预期的行为。) The remaining larger tests all provide value for longer-lived software, but the main concern becomes the maintainability of such tests as time increases.(其余大型测试都为生命周期较长的软件提供了价值,但随着时间的增加,主要的问题变成了这种测试的可维护性。) The rate of distinct scenarios to test in an end-to-end way can grow exponentially or combinatorially depending on the structure of the system under test, and that growth does not scale. Therefore, as the system grows, we must find alternative larger testing strategies to keep things manageable.(以端到端的方式测试的不同场景的速率可以指数增长或组合增长,这取决于被测系统的结构,并且这种增长无法扩展。因此,随着系统的发展,我们必须找到其他大型测试的测试策略,以保持测试的可管理性。)
by kevin
20.
十月 2022 18:27
>
Writing testable code requires an upfront investment. It is especially critical early in the lifetime of a codebase because the later testability is taken into account, the more difficult it is to apply to a codebase. Code written without testing in mind typically needs to be refactored or rewritten before you can add appropriate tests.(编写可测试代码需要前期投入。在代码库生命周期的早期,这一点尤其重要,因为越晚考虑可测试性,就越难应用到代码库中。在没有考虑到测试的情况下编写的代码通常需要重构或重写,然后才可以添加适当的测试。) Preferring real implementations in tests is known as classical testing. There is also a style of testing known as mockist testing, in which the preference is to use mocking frameworks instead of real implementations. Even though some people in the software industry practice mockist testing (including the creators of the first mocking frameworks), at Google, we have found that this style of testing is difficult to scale. It requires engineers to follow strict guidelines when designing the system under test, and the default behavior of most engineers at Google has been to write code in a way that is more suitable for the classical testing style.(在测试中更倾向于使用真实实现被称为经典测试。还有一种测试风格被称为模拟测试,其中倾向于使用模拟框架而不是真实实现。尽管软件行业的一些人在进行模拟测试(包括第一个模拟框架的创造者),但在谷歌,我们发现这种测试风格很难扩展。它要求工程师遵循设计被测系统时的严格准则,而谷歌大多数工程师的默认行为是以一种更适合经典测试风格的方式来编写代码。) To reduce the number of fakes that need to be maintained, a fake should typically be created only at the root of the code that isn’t feasible for use in tests. For example, if a database can’t be used in tests, a fake should exist for the database API itself rather than for each class that calls the database API.(为了减少需要维护的伪造测试代码的数量,伪造测试代码通常应该只在测试中不可行的代码根处创建。例如,如果一个数据库不能在测试中使用,那么应该为数据库API本身编写一个伪造测试,而不是为调用数据库API的每个类编写。) 真实实现应优先于测试替代。 如果在测试中不能使用真实实现,那么伪造实现通常是理想的解决方案。 过度使用打桩会导致测试不明确和变脆。 在可能的情况下,应避免交互测试:因为交互测试会暴露被测系统的实现细节,所以会导致测试不连贯。
by kevin
11.
十月 2022 13:41
>
At Google, we’ve found that engineers sometimes need to be persuaded that testing via public APIs is better than testing against implementation details. The reluctance is understandable because it’s often much easier to write tests focused on the piece of code you just wrote rather than figuring out how that code affects the system as a whole. Nevertheless, we have found it valuable to encourage such practices, as the extra upfront effort pays for itself many times over in reduced maintenance burden. Testing against public APIs won’t completely prevent brittleness, but it’s the most important thing you can do to ensure that your tests fail only in the event of meaningful changes to your system.(在谷歌,我们发现工程师有时需要被说服,通过公共API进行测试比针对实现细节进行测试要好。这种不情愿的态度是可以理解的,因为写测试的重点往往是你刚刚写的那段代码,而不是弄清楚这段代码是如何影响整个系统的。然而,我们发现鼓励这种做法是很有价值的,因为额外的前期努力在减少维护负担方面得到了许多倍的回报。针对公共API的测试并不能完全防止脆性,但这是你能做的最重要的事情,以确保你的测试只在系统发生有意义的变化时才失败。) The most common reason for problematic interaction tests is an over reliance on mocking frameworks. These frameworks make it easy to create test doubles that record and verify every call made against them, and to use those doubles in place of real objects in tests. This strategy leads directly to brittle interaction tests, and so we tend to prefer the use of real objects in favor of mocked objects, as long as the real objects are fast and deterministic.(交互测试出现问题的最常见原因是过度依赖mocking框架。这些框架可以很容易地创建测试替换,记录并验证针对它们的每个调用,并在测试中使用这些替换来代替真实对象。这种策略直接导致了脆弱的交互测试,因此我们倾向于使用真实对象而不是模拟对象,只要真实对象是快速和确定的。) The problem is that framing tests around methods can naturally encourage unclear tests because a single method often does a few different things under the hood and might have several tricky edge and corner cases. There’s a better way: rather than writing a test for each method, write a test for each behavior.[^4] A behavior is any guarantee that a system makes about how it will respond to a series of inputs while in a particular state.[^5] Behaviors can often be expressed using the words “given,” “when,” and “then”: “Given that a bank account is empty, when attempting to withdraw money from it, then the transaction is rejected.” The mapping between methods and behaviors is many-to-many: most nontrivial methods implement multiple behaviors, and some behaviors rely on the interaction of multiple methods. (问题是,围绕方法测试框架自然会鼓励不清晰测试,因为单个方法经常在背后下做一些不同的事情,可能有几个棘手的边缘和角落的情况。有一个更好的方法:与其为每个方法写一个测试,不如为每个行为写一个测试。 行为是一个系统对它在特定状态下如何响应一系列输入的任何保证。"鉴于一个银行账户是空的,当试图从该账户中取钱时,该交易被拒绝。" 方法和行为之间的映射是多对多的:大多数不重要的方法实现了多个行为,一些行为依赖于多个方法的交互。) If humans are bad at spotting bugs from string concatenation, we’re even worse at spotting bugs that come from more sophisticated programming constructs like loops and conditionals. The lesson is clear: in test code, stick to straight-line code over clever logic, and consider tolerating some duplication when it makes the test more descriptive and meaningful. We’ll discuss ideas around duplication and code sharing later in this chapter.(如果人类不善于发现来自字符串连接的错误,那么我们更不善于发现来自更复杂的编程结构的错误,如循环和条件。这个教训很清晰:在测试代码中,坚持使用直线代码而不是复杂的逻辑,并在测试更具描述性的时候考虑容忍一些重复。我们将在本章后面讨论关于重复和代码共享的想法。)
by kevin
1.
十月 2022 13:43
>
Unlike the QA processes of yore, in which rooms of dedicated software testers pored over new versions of a system, exercising every possible behavior, the engineers who build systems today play an active and integral role in writing and running automated tests for their own code. Even in companies where QA is a prominent organization, developer-written tests are commonplace. At the speed and scale that today’s systems are being developed, the only way to keep up is by sharing the development of tests around the entire engineering staff.(与过去的QA过程不同的是,在过去的QA过程中,专门的软件测试人员仔细研究系统的新版本,练习各种可能的行为,而今天构建系统的工程师在为自己的代码编写和运行自动测试方面发挥着积极和不可或缺的作用。即使在QA是一个重要组织的公司,开发人员编写的测试也是很常见的。以当今系统开发的速度和规模,跟上的唯一方法是与整个工程人员共享测试开发。) 对于来自没有强大测试文化的组织的开发者来说,把编写测试作为提高生产力和速度的手段的想法可能看起来是对立的。毕竟,编写测试所需的时间(如果不是更长的话!)可能与实现功能所需的时间一样长。相反,在谷歌,我们发现投入于软件测试对开发人员的生产力有几个关键的好处: 更少的调试 正如你所期望的那样,经过测试的代码在提交时有更少的缺陷。重要的是,它在整个存在过程中也有较少的缺陷;大多数缺陷在代码提交之前就会被发现。在谷歌,一段代码在其生命周期内预计会被修改几十次。它将被其他团队甚至是自动代码维护系统所改变。一次写好的测试会继续带来红利,并在项目的生命周期中防止昂贵的缺陷和恼人的调试过程。对项目或项目的依赖关系的改变,如果破坏了测试,可以被测试基础设施迅速发现,并在问题被发布到生产中之前回滚。 在变更中增加了信心 所有的软件变更。具有良好测试的团队可以满怀信心地审查和接受项目的变更,因为他们的项目的所有重要行为都得到了持续验证。这样的项目鼓励重构。在保留现有行为的情况下,重构代码的变化应该(最好)不需要改变现有的测试。 改进文档 软件文档是出了名的不可靠。从过时的需求到缺失的边缘案例,文档与代码的关系很脆弱,这很常见。清晰的、有针对性的测试,一次行使一个行为的功能是可执行的文档。如果你想知道代码在某一特定情况下做了什么,看看该情况的测试。更好的是,当需求发生变化,新的代码破坏了现有的测试时,我们会得到一个明确的信号,"文档 "现在已经过时了。请注意,只有在注意保持测试的清晰和简洁的情况下,测试才能作为文档发挥最佳效果。 简单审查 在谷歌,所有的代码在提交之前都要经过至少一名其他工程师的审查(详见第九章)。如果代码审查包括彻底的测试,证明代码的正确性、边缘情况和错误情况,那么代码审查员花在验证代码是否按预期运行的精力就会减少。审查员可以验证每个案例都有一个合格的测试,而不必费心费力地在代码中对每个案例进行解读。 深思熟虑设计 为新代码编写测试是锻炼代码本身的API设计的一种实用手段。如果新代码难以测试,往往是因为被测试的代码有太多的职责或难以管理的依赖关系。设计良好的代码应该是模块化的,避免紧密耦合,并专注于特定的责任。尽早修复设计问题往往意味着以后的返工更少。 快速、高质量的发布 有了健康的自动化测试套件,团队可以放心地发布新版本的应用程序。谷歌的许多项目每天都会向生产部门发布一个新的版本--即使是有数百名工程师的大型项目,每天都会提交成千上万的代码修改。如果没有自动化测试,这是不可能的。 One of the lessons we learned fairly early on is that engineers favored writing larger, system-scale tests, but that these tests were slower, less reliable, and more difficult to debug than smaller tests. Engineers, fed up with debugging the system-scale tests, asked themselves, “Why can’t we just test one server at a time?” or, “Why do we need to test a whole server at once? We could test smaller modules individually.” Eventually, the desire to reduce pain led teams to develop smaller and smaller tests, which turned out to be faster, more stable, and generally less painful.(我们很早就学到的一个经验是,工程师们喜欢写更大的、系统规模的测试,但这些测试比小型测试更慢,更不可靠,更难调试。工程师们厌倦了调试系统规模的测试,问自己:"为什么我们不能一次只测试一台服务器?"或者,"为什么我们需要一次测试整个服务器?我们可以单独测试较小的模块"。最终,减轻痛苦的愿望促使团队开发越来越小的测试,结果证明,这些测试更快、更稳定,而且通常也更少痛苦。) 在谷歌,我们把每一个测试都归为一个规模,并鼓励工程师总是为一个给定的功能编写尽可能小的测试。一个测试的规模大小不是由它的代码行数决定的,而是由它的运行方式、它被允许做什么以及它消耗多少资源决定的。事实上,在某些情况下,我们对小、中、大的定义实际上被编码为测试基础设施可以在测试上执行的约束。我们稍后会讨论细节,但简单地说,小型测试在一个进程中运行,中型测试在一台机器上运行,而大型测试在任何地方运行。 Code coverage is a measure of which lines of feature code are exercised by which tests. If you have 100 lines of code and your tests execute 90 of them, you have 90% code coverage.[^7] Code coverage is often held up as the gold standard metric for understanding test quality, and that is somewhat unfortunate. It is possible to exercise a lot of lines of code with a few tests, never checking that each line is doing anything useful. That’s because code coverage only measures that a line was invoked, not what happened as a result. (We recommend only measuring coverage from small tests to avoid coverage inflation that occurs when executing larger tests.)(代码覆盖率是衡量哪些特征代码行被哪些测试所执行的标准。如果你有100行代码,你的测试执行了其中的90行,你就有90%的代码覆盖率。 代码覆盖率经常被认为是理解测试质量的黄金标准,这是很不幸的。有可能用几个测试来验证大量的代码行,但从未检查过每一行是否在做任何有用的事情。这是因为代码覆盖率只衡量一行被调用的情况,而不是结果。(我们建议只测量小型测试的覆盖率,以避免执行大型测试时出现覆盖率膨胀)。)
by kevin
29.
九月 2022 08:28
>
Instead, before you begin writing, you should (formally or informally) identify the audience(s) your documents need to satisfy. A design document might need to persuade decision makers. A tutorial might need to provide very explicit instructions to someone utterly unfamiliar with your codebase. An API might need to provide complete and accurate reference information for any users of that API, be they experts or novices. Always try to identify a primary audience and write to that audience.(相反,在你开始写作之前,你应该(正式地或非正式地)确定你的文件需要满足的受众。设计文档可能需要说服决策者。教程可能需要为完全不熟悉你的代码库的人提供非常明确的说明。API可能需要为该API的任何用户(无论是专家还是新手)提供完整准确的参考信息。始终尝试确定主要受众并为该受众写作。) In some cases, different audiences require different writing styles, but in most cases, the trick is to write in a way that applies as broadly to your different audience groups as possible. Often, you will need to explain a complex topic to both an expert and a novice. Writing for the expert with domain knowledge may allow you to cut corners, but you’ll confuse the novice; conversely, explaining everything in detail to the novice will doubtless annoy the expert.(在某些情况下,不同的受众需要不同的写作风格,但在大多数情况下,技巧是以一种尽可能广泛地适用于不同受众群体的方式进行写作。通常,你需要同时向专家和新手解释一个复杂的主题。为有领域知识的专家写作方式可能会让你少走弯路,但你会让新手感到困惑;反之,向新手详细解释一切无疑会让专家感到厌烦。) A good design document should cover the goals of the design, its implementation strategy, and propose key design decisions with an emphasis on their individual trade-offs. The best design documents suggest design goals and cover alternative designs, denoting their strong and weak points.(一个好的设计文档应该包括设计目标、实施策略,并提出关键的设计决策,重点放在它们各自的权衡上。最好的设计文档建议设计目标,涵盖替代设计,指出其优缺点。) Importantly, if documentation is tied into the engineering workflow, it will often improve over time. Most documents at Google now implicitly go through an audience review because at some point, their audience will be using them, and hopefully letting you know when they aren’t working (via bugs or other forms of feedback).(重要的是,如果文档与工程工作流程联系在一起,它往往会随着时间的推移而改进。现在,谷歌的大多数文档都隐式地经过受众审查,因为在某个时候,他们的读者会使用这些文档,并希望在它们不起作用时(通过bug或其他形式的反馈)让你知道。) To change the quality of engineering documentation, engineers—and the entire engineering organization—need to accept that they are both the problem and the solution. Rather than throw up their hands at the state of documentation, they need to realize that producing quality documentation is part of their job and saves them time and effort in the long run. For any piece of code that you expect to live more than a few months, the extra cycles you put in documenting that code will not only help others, it will help you maintain that code as well.(为了改变工程文档的质量,工程师和整个工程组织需要接受他们既是问题又是解决方案。他们需要意识到,制作高质量的文档是他们工作的一部分,从长远来看,这可以节省他们的时间和精力,而不是在当前文档状态下束手无策。对于任何一段生命周期超过几个月的代码,记录该代码的额外周期不仅有助于其他人,也有助于维护该代码。)
by kevin
27.
九月 2022 13:56
>
Google’s culture, like that of a lot of software companies, is based on giving engineers wide latitude in how they do their jobs. There is a recognition that strict processes tend not to work well for a dynamic company needing to respond quickly to new technologies, and that bureaucratic rules tend not to work well with creative professionals. Code review, however, is a mandate, one of the few blanket processes in which all software engineers at Google must participate. Google requires code review for almost[^4] every code change to the codebase, no matter how small. This mandate does have a cost and effect on engineering velocity given that it does slow down the introduction of new code into a codebase and can impact time-to-production for any given code change. (Both of these are common complaints by software engineers of strict code review processes.) Why, then, do we require this process? Why do we believe that this is a long-term benefit?(谷歌的文化,就像许多软件公司的文化一样,是基于给工程师们在工作中的自由度。人们认识到,对于需要对新技术做出快速反应的充满活力的公司来说,严格的流程往往不起作用,而官僚主义的规则往往不适合创造性专业人士。然而,代码审查是一项任务,是谷歌所有软件工程师都必须参与的少数全流程之一。谷歌要求对代码库的每一次代码修改都要进行代码审查,无论多么微小。这个任务确实对工程速度有成本和影响,因为它确实减缓了将新代码引入代码库的速度,并可能影响任何特定代码更改的生产时间。(这两点是软件工程师对严格的代码审查过程的常见抱怨)。那么,为什么我们要要求这个过程?为什么我们相信这是一个长期有利的?) 一个精心设计的代码审查过程和认真对待代码审查的文化会带来以下好处: 检查代码的正确性 确保其他工程师能够理解代码更改 强化整个代码库的一致性 从心理上促进团队的所有权 实现知识共享 提供代码审查本身的历史记录 Surprisingly enough, checking for code correctness is not the primary benefit Google accrues from the process of code review. Checking for code correctness generally ensures that a change works, but more importance is attached to ensuring that a code change is understandable and makes sense over time and as the codebase itself scales. To evaluate those aspects, we need to look at factors other than whether the code is simply logically “correct” or understood.(令人惊讶的是,检查代码的正确性并不是谷歌从代码审查过程中获得的最大好处。检查代码正确性通常可以确保更改有效,但更重要的是确保代码更改是可以理解的,并且随着时间的推移和代码库本身的扩展而变得有意义。为了评估这些方面,我们需要查看除代码在逻辑上是否“正确”或理解之外的其他因素。) Code review also has important cultural benefits: it reinforces to software engineers that code is not “theirs” but in fact part of a collective enterprise. Such psychological benefits can be subtle but are still important. Without code review, most engineers would naturally gravitate toward personal style and their own approach to software design. The code review process forces an author to not only let others have input, but to compromise for the sake of the greater good.(代码审查还有重要的文化好处:它向软件工程师强调代码不是“他们的”,而是事实上集体事业的一部分。这种心理上的好处可能很微妙,但仍然很重要。没有代码审查,大多数工程师自然会倾向于个人风格和他们自己的软件设计方法。代码审查过程迫使作者不仅要让别人提出意见,而且要为了更大的利益做出妥协。) Part of the code review process of feedback and confirmation involves asking questions on why the change is done in a particular way. This exchange of information facilitates knowledge sharing. In fact, many code reviews involve an exchange of information both ways: the authors as well as the reviewers can learn new techniques and patterns from code review. At Google, reviewers may even directly share suggested edits with an author within the code review tool itself.(反馈和确认的代码评审过程的一部分包括询问为什么以特定方式进行更改。这种信息交流有助于知识共享。事实上,许多代码评审都涉及双向信息交换:作者和审查员都可以从代码评审中学习新的技术和模式。在谷歌,审查员甚至可以直接在代码审查工具中与作者分享建议的编辑。) In general, reviewers should defer to authors on particular approaches and only point out alternatives if the author’s approach is deficient. If an author can demonstrate that several approaches are equally valid, the reviewer should accept the preference of the author. Even in those cases, if defects are found in an approach, consider the review a learning opportunity (for both sides!). All comments should remain strictly professional. Reviewers should be careful about jumping to conclusions based on a code author’s particular approach. It’s better to ask questions on why something was done the way it was before assuming that approach is wrong.(一般来说,审查员应该在特定的方法上听从作者的意见,只有在作者的方法有缺陷时才指出替代方法。如果作者能证明几种方法同样有效,审查员应该接受作者的偏好。即使在这些情况下,如果发现一个方法有缺陷,也要把审查看作是一个学习的机会(对双方都是如此!)。所有的评论都应该严格保持专业性。审查员应该注意不要根据代码作者的特定方法就下结论。在假设该方法是错误的之前,最好先问一下为什么要这样做。) As much as we expect professionalism on the part of the reviewer, we expect professionalism on the part of the author as well. Remember that you are not your code, and that this change you propose is not “yours” but the team’s. After you check that piece of code into the codebase, it is no longer yours in any case. Be receptive to questions on your approach, and be prepared to explain why you did things in certain ways. Remember that part of the responsibility of an author is to make sure this code is understandable and maintainable for the future.(就像我们期望审查员有专业精神一样,我们也期望作者有专业精神。记住,你不是你的代码,你提出的这个修改不是 "你的",而是团队的。在你把这段代码检查到代码库中后,它无论如何都不再是你的了。要乐于接受关于你的方法的问题,并准备好解释你为什么以某种方式做事情。记住,作者的部分责任是确保这段代码是可以理解的,并且可以为将来维护。) It’s important to treat each reviewer comment within a code review as a TODO item; a particular comment might not need to be accepted without question, but it should at least be addressed. If you disagree with a reviewer’s comment, let them know, and let them know why and don’t mark a comment as resolved until each side has had a chance to offer alternatives. One common way to keep such debates civil if an author doesn’t agree with a reviewer is to offer an alternative and ask the reviewer to PTAL (please take another look). Remember that code review is a learning opportunity for both the reviewer and the author. That insight often helps to mitigate any chances for disagreement.(重要的是要把代码审查中的每个审查者的评论当作一个TODO项目;一个特定的评论可能不需要被无条件接受,但它至少应该被解决。如果你不同意一个评审员的评论,让他们知道,并让他们知道为什么,在双方都有机会提供替代方案之前,不要把评论标记为已解决。如果作者不同意审查员的意见,保持这种辩论的一个常见方法是提供一个替代方案,并要求评审员PTAL(请再看看)。记住,代码审查对于审查者和作者来说都是一个学习的机会。这种洞察力往往有助于减少任何分歧的场景。)