Google软件工程之过程篇

Linux阅码场 2022-09-23 08:05

上篇[1]介绍了Google软件工程中的文化部分,本篇介绍软件工程中主要的过程部分,包括编码风格指南、代码评审、技术文档、自动化测试(单元测试、集成测试与较大型测试)与弃用。

以下是《Software Engineering at Google》一书第三部分过程篇的思维导图,由于此部分占全书近40%,所以本文不会详细的介绍其中的概念,想详细了解的读者建议阅读原书。本文会结合此书这部分内容分享作者的个人理解及相关经验。

风格指南(Style Guide)

We value “simple to read" over "simple to write." (Software Engineering at Google - Style Guides and Rules)

代码可能只会被写一次,但会被读很多次。如果团队成员的代码风格都不统一,可读性会很差,所以保持团队代码风格统一很重要。

历史证明,能写的很飘逸的编程语言使用人数一般都不会很多,典型的如古老的Perl语言,可以达到“一人千面”的代码风格。而写起来中规中矩甚至没有啥高级技巧的语言如Java、Go等在工业上反而用的很多。

Google的代码风格指南不太适合一般规模的公司,所以此部分不做过多介绍。从我的个人经验来说,一般项目上会配置一套自动化的代码风格检查工具(如checkstyle[2]),甚至会集成到流水线(Pipeline)中强制团队保持一致的代码风格。某些编程语言如Go在构建工具中也提供了gofmt的代码格式化工具。

代码风格指南只能解决一些很基本的可读性问题,如代码缩进、函数命名风格、代码行数限制等。但代码的可读性可不只体现在这些表面,更深层次的可读性问题如API语义的可读性该怎么解决?一个可行的实践是代码评审。

代码评审(Code Review)

代码评审是如此重要,以至于其在Google是必须做的一个实践过程。它能提供以下的好处:

代码正确性:评审人员可能发现评审代码中的逻辑问题,从而提前消除一些潜在的Bug;代码可读性:代码能否被其他人很容易的理解?API语义设计是否合理?是否包含测试?是否有必要的文档与注释?代码一致性:代码风格是否与团队和组织保持一致?促进团队知识共享:代码评审可以让团队其他成员了解你所做工作的上下文;塑造团队工程文化:团队保持代码评审的实践,本身也是团队工程文化的一部分,能让新的成员迅速适应团队工程文化;

代码评审的最佳实践有以下:

友善且专业小的变更清晰的变更描述小规模评审尽可能自动化金字塔模型

代码评审金字塔模型如下图所示:

代码评审的反模式是倒金字塔模型,也就是很多时间花费在了可以自动化执行的部分比如代码风格的统一、自动化测试等,但在金字塔模型里,代码评审应该把主要的精力放在API语义、实现语义及文档等部分。

Code Review v.s. Code Diff

Diff 和 Review 的区别在于前者是一个团队集体行动,团队成员一块看某个开发者前一天写的代码,这样的好处在于每个人都能反馈,也能了解其他人做的工作,防止一些信息不同步的问题。代码评审一般是一两个人(可能甚至是团队外部的人)去审查对方要合入主干分支的代码,更适合外部人员提交代码到主干这种 GitHub PR 分支管理模式。

我所在项目的团队每天会做 Code Diff ,这是个必须的实践。团队规模在几人以内可以让每个人都有时间讲解自己的代码,如果代码太多,那可以给每个人一个时间限制。如果团队太大那可以拆分成多个 stream 来管理,总之 Diff 的人员不能太多,但每天都应该花时间做,因为收益要高于成本,可以统一代码风格,保证可读性,提高成员技术水平。

技术文档(Technical Documentation)- Technical Documentatio

技术文档与代码一样应该得到开发者同等的重视,但有太多文档与代码不同步的场景出现,导致文档的可用性大大降低。为什么会出现这种问题?一方面是因为开发者重视度不够的问题,另外一方面是因为写一份好的技术文档并不是一件简单的事情。

如何写一份好的技术文档?推荐阅读如下的文章:

Technical Writing for Developers[3]Technical Writing | Google Developers[4]SEO Copywriting Guide[5]

开发人员不喜欢文档的另外一个原因在于,代码和文档的工作流程并不相同,一般文档都存放在与代码不同的位置,比如某个FTP目录以Word的格式存在。要是文档的编写可以和代码在同一套工作流里,就能极大的降低开发者的心智负担,这正是Docs-as-code[6]的设计理念,具体的流程实践可以看这篇文章:

Working in public — our docs-as-code approach[7]

测试(Testing)

测试是软件工程过程中很重要的一个组成部分,而这里的测试主要指自动化测试过程,人工测试占比很少。测试也有一个金字塔模型,如下图所示:

关于测试金字塔的细节,推荐阅读这篇文章:

The Practical Test Pyramid[8]

开发人员写自动化测试有如下好处:

更少的Debugging:有了自动化测试后,系统的很多行为可以通过测试代码观察到。当然Bug一旦产生说明测试代码覆盖不全面,需要补上相关的测试,久而久之,测试代码就形成了非常全面的防护网。提升对代码变更的信心:当有了测试防护网后,对代码一旦产生破坏性的更新,测试代码会失败,这就给开发人员机会在部署前去修复此问题。测试代码是更好的文档:当面对一个完全陌生的代码库时,除了有限的文档,另外一个了解系统行为的方式就是看测试代码。测试代码相比文档,有着更全面清晰的业务细节,能给予开发人员更多的信息去了解此业务系统。让代码评审更简单:测试代码相比生产代码更接近业务视角,能让评审人员从业务系统对外行为的视角去了解生产代码的意图。这样也能让评审人员做出更有效的反馈意见。好的测试反向提升代码设计:要让系统模块具备一定的测试性才能写出测试代码,所以有测试的代码从设计的角度看,其可读性与解耦度相比没有测试代码的要更高。敏捷实践中推崇的TDD(Test Driven Development)就是一种通过测试驱动出好的实现代码的实践。自动化测试让持续交付变的更容易:如果没有自动化测试的帮助,代码部署上去后出Bug的概率要更高,这会提高系统交付的时间。

没有测试代码的系统是遗留系统。

单元测试(Unit Testing)

单元测试作为占比测试金字塔最大部分的底座,重要性不言而喻。它的优势很多,但Google在多年的实践中发现,提高单元测试的可维护性非常重要。而难以维护的测试代码主要有两方面造成:

测试脆弱:当在代码重构、添加新特性及修复Bug时,会出现一些测试无法跑通,只能通过修改测试的方式来解决,这说明已有的测试很脆弱。好的测试应该只有在系统的业务行为发生改变时,才需要修改生产代码和测试代码。造成测试脆弱的原因有很多种,可能的原因包括测试隔离没做好,比如依赖了很多共享的全局性状态,或者测试了非公开的函数或方法,又或者测试的粒度过细,把很多实现细节给测试了。测试不清晰:不清晰的原因也有很多方面,比如测试的名称并没有体现其测试意图,在单个测试中测试了一些不必要的行为,又或包含了很多无关的信息。

要提高可维护性,一些好的实践包括以下方面:

测试行为而非方法:很多测试框架如Junit都倡导Given/When/Then三段式测试编写方式,这样可以从验收标准(Acceptance Criteria)的业务视角去编写测试,而非针对单个函数或方法去编写测试(这很容易写出脆弱的测试)。测试名称应提现测试行为:当单元测试失败时,最先看到的就是测试失败单元的名称,好的测试名称能以最直接的方式体现该测试意图,所以测试名称长一些也可以。测试不应包含逻辑:因为测试单元本身并没有额外的测试,如果测试包含了比较复杂的逻辑,可能会导致测试代码的Bug。所以测试代码中尽可能不包含逻辑计算的过程。DAMP(Descriptive And Meaningful Phrases)原则:在生产代码上业界倡导DRY[9](Don't repeat yourself)的基本原则。而在测试代码中,正如上面几条实践表明,一定程度上的代码冗余是有必要的,这能帮助我们编写出简单而清晰的测试代码。

单元测试的代码执行速度一定要快,但在要测试的生产代码中,可能包含了执行速度很慢的代码,比如网络或文件等I/O操作,又或者对数据库的请求,甚至需要整个应用启动来获得完整的执行环境。如何将这类慢的代码与真正要测试业务逻辑的代码隔离开来?那就是接下来要介绍的测试替身技术。

测试替身(Test Doubles)

测试替身能通过一些模拟或伪造的技术来控制被测试代码的执行路径,比如在OOP中我们可以通过接口的多个实现,来完成生产代码与测试代码的不同实现。

由于测试替身技术本身非常成熟,所以本文不做基本的介绍,推荐阅读这篇文章进一步了解:

TestDouble[10]

在Google的多年实践中发现,测试替身很容易被滥用,造成很多脆弱的测试,而被滥用最多的就是打桩(Stubbing)技术。不同替身技术都有其适用场景,推荐的一个决策流程是:

如果生产代码的执行时间足够快,那就不需要替身技术,直接测试生产代码;如果伪造(Faking)的实现成本很低,且伪造的保真度够高(能尽可能模拟真实的使用场景),则推荐使用伪造替身技术;如果在前两者都不可用的情况下,仅被测试代码只依赖少量函数或方法的返回值时,可以使用打桩(Stubbing)替身技术;交互测试(Interaction Testing)替身技术谨慎使用,如果要用也仅在需测试函数副作用或调用次序时使用,并且不要过度测试不必要的数据;

较大型的测试(Larger Testing)

在测试金字塔的顶端是占比只有20%的集成测试与E2E测试,虽然占比少,但其却可解决单元测试的以下问题:

保真度的问题:单元测试因使用测试替身来加速执行时间,但替身与实现本身就存在保真度的问题,一旦被替身的实现发生改变,单元测试因模拟行为未变,可能造成一些意想不到的Bugs。环境配置的问题:环境的问题只能在接近生产环境的测试环境(如UAT)环境中去测试与发现问题,这是单元测试无法覆盖的测试范围。如Google的一些重大全球性的Bug都和环境配置问题有关系。负载下的问题:在压力测试下,系统的行为表现如何?性能是否能达到业务要求?这类非功能性的需求测试只能在E2E测试中完成。预期外的行为与副作用:单元测试是在开发者预期的视角下完成的,所以存在一定的视角盲区。在一个接近生产环境的测试环境测试是发现这类问题最好的办法。紧急行为和真空效应:如果系统的运行时环境发生一些意外的修改,如集群网络配置或部署配置发生变更,这类问题也只能在集成环境中发现。

较大型测试的编写与维护都是成本高昂的,在我们项目实践中,一般和业务系统强相关的集成测试和部分E2E测试都是业务开发团队完成的。但一些公共的E2E测试,比如某个全局性的功能性测试,可能由一个独立的小组完成,也可能只完成一个MVP的版本,之后由业务系统维护团队开发完成。

推荐进一步阅读的文章:

浅谈契约测试[11]契约测试之核心解惑[12]

弃用(Deprecation)

代码是资产还是负债?Google的答案是负债,因为代码需要不断的维护才能正常工作。负债是有高昂的利息,降低负债最好的办法就是在不需要的时候砍掉它。而这就是弃用过程的价值。

对开发人员来说,弃用是个难以接受的过程,因为幸苦写的代码,很难下定决心去销毁它。所以一个中庸之道是在代码将要被弃用前,想办法通过演进的方式给予其二次生命。如果非要弃用,也只是停止维护和运行,旧的代码依旧会在代码仓库中可被搜索到,历史记录也会被保留。

我的个人项目实践是,下线一个系统是一件需要重视的过程。一个系统一旦被发布,它被使用的场景就很难以想象,API的用户可能会以意想不到的方式去使用它。所以尽可能通过代码搜索去找到其被使用的场景,之后再给充足的Deadline广而告之,甚至可以主动与用户沟通,确保不会让其出现大的损失。

总结

代码可能只会被写一次,但会被读很多次。所以软件工程中的过程部分主要致力于解决代码可读性的问题。无论是风格指南、代码评审、文档甚至自动化测试,很大程度上都在为提高代码可读性。

写代码很容易,能写出易懂的代码却有难度。所以从这个角度看,写代码是个入门简单精通却难的技能,需要我们不断的精进,通过多种实践去提高这个技能。希望这篇文章能让你对写代码这件事有更多的理解。

References

[1] 上篇: /dev/software-engineering-at-google/culture/
[2] checkstyle: https://github.com/checkstyle/checkstyle
[3] Technical Writing for Developers: https://css-tricks.com/technical-writing-for-developers/
[4] Technical Writing | Google Developers: https://developers.google.com/tech-writing/overview
[5] SEO Copywriting Guide: https://www.semrush.com/blog/seo-copywriting/
[6] Docs-as-code: https://www.writethedocs.org/guide/docs-as-code/
[7] Working in public — our docs-as-code approach: https://blog.cloudflare.com/our-docs-as-code-approach/
[8] The Practical Test Pyramid: https://martinfowler.com/articles/practical-test-pyramid.html
[9] DRY: https://zh.wikipedia.org/wiki/一次且仅一次
[10] TestDouble: https://martinfowler.com/bliki/TestDouble.html
[11] 浅谈契约测试: https://insights.thoughtworks.cn/contract-testing/
[12] 契约测试之核心解惑: https://ariman.cn/2019/05/19/契约测试之核心解惑/


Linux阅码场 专业的Linux技术社区和Linux操作系统学习平台,内容涉及Linux内核,Linux内存管理,Linux进程管理,Linux文件系统和IO,Linux性能调优,Linux设备驱动以及Linux虚拟化和云计算等各方各面.
评论
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 119浏览
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 109浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 71浏览
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 68浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 120浏览
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 111浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 100浏览
  • 学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&
    youyeye 2024-11-30 14:30 78浏览
  • TOF多区传感器: ND06   ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。                 如果用ND06进行手势识别,只需要经过三个步骤: 第一步&
    esad0 2024-12-04 11:20 50浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 101浏览
  • 《高速PCB设计经验规则应用实践》+PCB绘制学习与验证读书首先看目录,我感兴趣的是这一节;作者在书中列举了一条经典规则,然后进行详细分析,通过公式推导图表列举说明了传统的这一规则是受到电容加工特点影响的,在使用了MLCC陶瓷电容后这一条规则已经不再实用了。图书还列举了高速PCB设计需要的专业工具和仿真软件,当然由于篇幅所限,只是介绍了一点点设计步骤;我最感兴趣的部分还是元件布局的经验规则,在这里列举如下:在这里,演示一下,我根据书本知识进行电机驱动的布局:这也算知行合一吧。对于布局书中有一句:
    wuyu2009 2024-11-30 20:30 124浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 87浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 98浏览
  • 艾迈斯欧司朗全新“样片申请”小程序,逾160种LED、传感器、多芯片组合等产品样片一触即达。轻松3步完成申请,境内免费包邮到家!本期热荐性能显著提升的OSLON® Optimal,GF CSSRML.24ams OSRAM 基于最新芯片技术推出全新LED产品OSLON® Optimal系列,实现了显著的性能升级。该系列提供五种不同颜色的光源选项,包括Hyper Red(660 nm,PDN)、Red(640 nm)、Deep Blue(450 nm,PDN)、Far Red(730 nm)及Ho
    艾迈斯欧司朗 2024-11-29 16:55 174浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦