“尊重工艺”是 Instawork 工程团队的一项指导原则。它鼓励我们以学习的态度去看待软件的工艺。为了这个目的,我在去年建立了“工程图书俱乐部”,以帮助我们从行业中的优秀企业和优秀人才汲取经验。我们的阅读的第一本图书是 Software Engineering at Google(暂无中文版:《谷歌的软件工程》)。我们很想了解谷歌是如何以巨大的规模进行软件开发的:数十亿行的代码,成千上万的开发人员。他们的任何做法是否适合像我们这样的小型团队?
经过数月对这本书的研读和讨论,我们得出了一些很好的见解:
- 如众人所想的,谷歌的许多做法,在我们的规模上并没有意义。
- 不过,书中的许多想法对初创公司来说也是可行的。
- 实际上,Instawork 正在采纳谷歌的很多最佳实践!
在这个季度末,图书俱乐部提出了一份建议清单。我们应该开始做什么,还是继续做什么?以及在我们目前的规模下,我们应该等待做什么?
文档
谷歌像对待代码一样对待文档。文档是受版本控制的,要经过同行的评审,并定期更新/弃用。作者持有其文档,在其离职或者其身份变更时,应将其所有权转移。谷歌的内部文档管理系统还会根据文档最近的更新情况来展示文档的“新鲜度”。为了保证信息的准确性,系统会提醒作者有关旧文档的情况。
谷歌也着重指出,在编写文档时,要充分考虑到目标用户。比如,技术文档是为初级工程师编写的,它应该与为高级工程师编写的文档有很大的不同。初级工程师也许会觉得详尽的描述性说明非常有用,而高级工程师会感到冗长且多余。
我们的收获:
- 项目负责人将继续编写彻底的、经同行评审的设计文档,以供高级工程师审核。
- 我们将继续为新入职的员工编写简明的、适合初学者的指南。
- 我们应当从监控文档的新鲜度入手,摒弃陈旧的文档,并在编写时将目标受众纳入考量。
公平的工程
工程师编写的代码和它对用户的影响之间往往存在着脱节。设计和构建软件的团队不可能完全代表所有的产品用户。谷歌也遇到了这样的问题,因为他们的图像识别算法并没有针对足够多样化的人群进行训练,这导致用户的照片不能很好地根据肤色进行分类。他们建议工程师们至少要了解他们用户的人口统计数据。这可以帮助工程师更加认真地考虑他们所做的改动,并且考虑到这些改动是否会给某些人产生积极影响,也会给别人产生消极影响。
与团队成员讨论这一部分内容时,大家特别有见地。在 Instawork,我们致力于为当地企业和专业人士创造商机。促进公平的工程是我们工作的核心,但也总存在着改善的空间。
- 因为我们很多用户不是以英语为母语的人,所以我们必须把国际化和优秀的翻译放在第一位。
- 我们的用户往往拥有较旧的、资源受限的移动设备,因此,我们需要跟踪应用程序的规模及数据消耗来维持其易用性。
- 为了避免自己的偏见,我们会持续采访用户并查看数据。例如,我们假定我们的通知数量过多,但是数据和研究显示,我们的用户并不在意,实际上,他们是依靠这些通知来选择最佳班次。
- 我们将开始检查无意识的偏见,作为设计阶段的一部分,以确保我们的变更不会无意间伤害到任何用户。
知识共享与团队合作
软件工程中的协作即使不比个人工作更重要,也同样重要。代码是由个人开发者编写的,但产品是由团队构建的。团队在沟通和协作方面做得越好,他们的表现就越好。谷歌建议,要营造一种有心理安全感的文化,在这种文化氛围中,工程师能够坦然面对失败、不明白的事物,并提出问题。没有了心理安全感,工程师就不太可能因为害怕公开失败而去冒险。在论坛和 Slack 频道里,为了避免因不知道而显得愚蠢,因此都不去提问题。这种模式会阻碍学习,并且对工程师来说是有害的。在谷歌,团队之间的社交互动都是基于谦逊(我并非无懈可击,愿意改进)、尊重(我真心关注我的同事)、以及信任(我认为我的同事都很有能力,而且会作出正确的决定)。这就为工程师提供了一个框架,让他们能够正确地给予和接受批评,并且避免了一种指责文化,那就是团队成员会把过错归咎于谁。
我们很高兴地发现,谷歌很多以团队为导向的实践已经深深扎根在 Instawork 的文化中。
- 在进行代码审查时,我们默认会提出澄清性问题,而非陈述观点或假设错误。我们会以一种谦逊的态度来进行技术讨论。
- 我们会继续坚持无责的事后总结文化:在遇到重大问题时,我们会寻找解决方案,而非责备。
- 我们将会为 Slack 频道中的专家进行宣传,如 #mobile、#backend、#frontend、#infrastructure。这可以减少提问的摩擦,并在精神上增加一份安全感。
- 我们会不断构建全面的文档,以拓展 Instawork 的知识,方法是找出当前文档的差距,并找到所有者来弥补这些差距。
代码搜索
现代 IDE 能够搜索符号定义和引用,提交历史等。但是,IDE 会被谷歌 20 多亿行的单体仓库所“吞噬”。因此,谷歌开发了一个内部工具,可以在自己庞大的代码库上进行代码搜索。虽然现在的工程师还在利用 IDE 的特性来帮助编写代码,但是他们还是会利用代码搜索来阅读、理解和搜索代码。(本着谷歌的精神,)搜索结果是有排名的,这样,开发人员可以通过他们的搜索关键词来判断哪些代码最流行。如果搜索结果没有排名,那么开发人员就必须对成千上万(甚至数百万)的结果进行分析。随着代码库的发展,这种排名有助于促进更多的最新模式,并且可以发现那些可能准备废弃的代码。
对于谷歌这种巨大的代码库来说,开发一种单独的工具是很有必要的。我们一致认为,IDE 可以提供很多这样的特性:符号查询、引用、定义,都是期望的标准,大多数工程团队不需要更多的东西。在 IDE 中可以访问这些特性中的大多数更方便,并且不需要维护另一个应用程序。但是,我们更喜欢的是排名的搜索结果。IDE 只能对一个特定的资源库进行分析,而无法对其进行推断开发人员是如何使用它的。排名的搜索结果提高了工程师发现相关代码示例的概率,同时忽略了潜在的死代码。
- 我们会持续地利用和强化 IDE 工具,用于代码完成、导航和搜索。
- 一旦我们的规模需要,我们将等待引入像 Sourcegraph 这样更强大的代码搜索。
持续集成
持续集成是一种流程,它了解在开发的各个阶段都要进行什么样的测试。我们越早找到 Bug,修复 Bug 的成本就能做到越低。在编译过程中出现的错误将被迅速地找出来。等待测试套件所花费的时间更长,或者更糟糕:捕获生产环境中的 Bug。谷歌提倡迅速的反馈循环,以便尽早发现 Bug。在一个完美的世界里,所有的测试都是在编译时进行的。但是对于谷歌的规模来说,这样做是行不通的:数以百万计的单元测试和集成测试无法在一台计算机上进行。甚至在云端中并发进行测试,对他们来说也不够快。所以,谷歌在“提交前”(当 PR 代码更改时)进行一套很小的、重要的测试,而另一些则在“提交后”(当代码被合并到 main 时)进行。提交前进行的测试既快速又可靠。提交后进行的测试会变得缓慢,并且会对多个子项目进行测试,而且还会有些不稳定。权衡利弊:提交前的测试会丢失测试覆盖率以加快速度,而在提交后的测试会弥补这些测试空白。
谷歌之所以维护独立的测试套件,是出于他们庞大的代码库的需要。从 Instawork 的规模来看,我们并不认为采取这样的方式会带来任何好处。我们的全部单元测试套件大约需要 15 分钟的时间,考虑到我们每次都能获得 100% 的测试覆盖率,这对于我们的需求来说,是完全合理的。
- 我们将继续在提交前运行我们的整个测试套件。
- 我们将继续监控测试套件的执行时间以保持快速。
- 一旦这变得不可行,我们将等待建立一个 Staging 阶段的 CI 流程(提交前-提交后)。
结束语
Software Engineering at Google 这本书是我们工程图书俱乐部的绝佳选择,使我们的团队获益良多。这本书的主题与我们产生了共鸣:软件工程是随着时间推移而整合的编程。作为软件工程师,我们不只是负责编写代码。我们还需要迭代团队能够在代码的生命周期内为代码库作出贡献。
鉴于谷歌的庞大规模和悠久历史,他们采取了一系列的措施来保证代码质量和工程生产力。Instawork 作为一家初创公司,它还处在起步阶段:我们最古老的代码只存在了 5 年,而我们的产品却在飞速地发展。我们每一年都会变得更加成熟。随着上百万的用户依赖于我们的产品而获取商机,他们的投资将会不断增加。当我们的团队和代码库越来越成熟的时候,我们自然会像谷歌一样采取同样的做法。令人欣慰的是,我们正处于相同的软件工程旅程中。展望未来,随着我们团队和代码库的扩大,我们很高兴能引入更多的实践。
作者简介:
John Torres,Instawork 软件工程师。
原文链接:
https://engineering.instawork.com/do-googles-engineering-practices-work-for-a-startup-6b9a3b6b0ad7