原文链接:https://qiangmzsx.github.io/Software-Engineering-at-Google/#/?id=software-engineering-at-google
前言
软件工程 software engineering
编程 programming
计算机科学 computer science
程序员 programmer
软件工程师 software engineer
随时间变化的编程
软件工程是"随着时间推移而整合的编程"。我们可以在我们的代码中引入哪些实践,使其可持续--能够对必要的变化做出反应--在其生命周期中,从设计到引入到维护到废弃?
可维护,可扩展 -> 可持续
本书强调了三个原则,我们认为软件组织在设计、架构和编写代码时应该牢记这些原则:时间和变化 代码如何在其生命周期进行适配。规模和增长 一个组织如何适应它的演变过程。权衡和成本 一个组织如何根据时间和变化以及规模和增长的经验教训做出决策。
谷歌的视角
谷歌软件工程领域的三个主要方面:
- 文化:强调软件开发企业的集体性,软件开发是一项团队工作,正确的文化原则对于一个组织的成长和保持健康至关重要
- 过程
- 工具:利用对工具基础设施的投入来优化代码库,因为它既增长又腐化
第一章 软件工程是什么?
编程和软件工程之间有三个关键的区别:时间、规模和权衡取舍。在一个软件工程项目中,工程师需要更多关注时间成本和需求变更。在软件工程中,我们需要更加关注规模和效率,无论是对我们生产的软件,还是对生产软件的组织。最后,作为软件工程师,我们被要求作出更复杂的决策,其结果风险更大,而且往往是基于对时间和规模增长的不确定性的预估。
软件工程是随着时间推移的编程。时间的增加为编程增加了一个重要的新维度。
了解时间对程序的影响的一种方法是思考"代码的预期生命周期是多少?"。对于一个只需要存货一小时的程序,你不太可能考虑其底层库、操作系统(OS)、硬件或语言版本的新版本。
我们不是指"开发生命周期",而是指"维护生命周期"--代码将持续构建、执行和维护多长时间?这个软件提供多长时间的价值?
另一种看待软件工程的方法是考虑规模。有多少人参与?随着时间的推移,他们在开发和维护中扮演什么角色?编程任务通常是个人的创造行为,但软件工程任务是团队的工作。早期定义软件工程的尝试为这一观点提供了一个很好的定义:“多人开发的多版本程序”。这表明软件工程和程序设计之间的区别是时间和人的区别。团队协作带来了新的问题,但也提供了比任何单个程序员更多的潜力来产生有价值的系统。
团队组织、项目组成以及软件项目的策略和实践都支配着软件工程复杂性。
我们还可以说,软件工程与编程的不同之处在于需要做出的决策的复杂性及其风险。在软件工程中,我们经常被迫在几个路径之间做评估和权衡,有时风险很高,而且价值指标不完善。软件工程师或软件工厂负责人的工作目标是实现组织、产品和开发工作流程的可持续性和管理扩展成本为目标。考虑到这些投入,评估你的权衡并作出理性的决定。有时,我们可能会推迟维护更改,甚至接受扩展性不好的策略,因为我们知道需要重新审视这些决策。这些决策应该是明确的和清晰的递延成本。
递延成本是在交易中发生的成本,但之前不会被未来的会计期间列支。递延成本的一个例子是必要的费用,注册一个新的债券发行。 A公司可能将不得不支付律师和会计师的编制和审核由政府机构所要求的许多发言。 当这些费用是显着的,它们记录在长期资产帐户, 债券发行成本未摊销债券发行成本列为递延费用。 递延费用的数额,然后将是债券发行的债券的生活成本费用摊销(系统收费)。
时间与变化
海勒姆定律
:当一个API有足够的用户的时候,在约定中你承诺的什么都无所谓,所有在你系统里面被观察到的行为都会被一些用户直接依赖。
规模和效率
计算、内存、存储和带宽
许多问题,如"完成完整构建需要多长时间?"、“拉一个新的版本库需要多长时间?”或"升级到新语言版本需要多少成本?"都没有受到有效的监管,并且效率变得缓慢。这些问题很容易地变得温水煮青蛙,问题很容易慢慢恶化,而不会表现为单一的危机时刻。只有在整个组织范围内提高意识并致力于扩大规模,才能保持对这些问题的关注。
示例:编译器升级
- 专业知识:我们知道如何做到这一点,对于某些语言,我们现在已经在很多平台上进行了数百次编译器升级
- 稳定性:版本之间的更改更少,因为我们更优规律的采用版本,对于某些语言,我们现在每一到两周进行一次编译器升级部署
- 一致性:没有经过升级的代码更少了,这也是因为我们正在定期升级
- 熟悉:因为我们经常这样做,所以我们可以在执行升级的过程中发现冗余并尝试自动化。这是与SRE观点一致的地方
- 策略:我们有类似碧昂丝规则的策略。这些程序的净效果是:升级仍然是可行的。因为基础设施团队不需要担心每一个未知的使用,只需要担心我们的CI系统中常规的使用
左移
在开发人员的工作流程中发现的问题,通常可以降低成本。考虑开发人员工作流程的时间表,从左到右,从概念和设计开始,通过实施、评审、测试、提交、金丝雀和最终的生产部署来进行。在此时间线之前,将问题发现转移到"左侧"会使问题解决成本更低。
同样的基本模式在本书中多次出现。在提交之前通过静态分析(sonarqube?)和代码审查发现的bug要比投入生产的bug成本更低。在开发过程的早期提供高质量、可靠性和安全性的工具和实践使我们很多基础架构团队的共同目标。
权衡和成本
- 财务成本(金钱)
- 资源成本(如CPU时间)
- 人员成本(例如:工作量)
- 交易成本(例如,采取行动的成本是多少?)
- 机会成本(例如,不采取行动的成本是多少?)
- 社会成本(例如,这个选择将对整个社会产生什么影响?)
除了上述的成本,还有一些偏差:
- 保持现状偏差
- 损失厌恶偏差(人们面对同样的损失和收益时感到损失对情绪影响更大)
示例:标记
我们的目标是对我们所做的每件事都有同样程度的关注和明确的成本/收益权衡
对决策的输入
当我们权衡数据时,我们发现两种常见情况:
- 所有涉及的数量都是可测量的或至少可以预估的
- 有些数量是微妙的,或者我们不知道如何测量
重审决策,标记错误
在某个时候,将根据现有的数据做出决定-希望是基于准确的数据和仅有的几个假设,但隐含的是基于目前可用的数据。随着新数据的出现,环境的变化,或假设的不成立,可能会发现某个决策是错误的,或在当时是有意义的,但现在没有意义了。
我们坚信数据能为决策提供信息,但我们也认识到数据会随着时间的推移而变化,新数据可能会出现。这意味着,本质上,在相关系统的生命周期内,需要不时地重新审视决策。对于长期项目而言,在做出决策后,有能力改变方向通常是至关重要的。
第二章 如何融入团队
首先从聚焦与一个你完全控制的变量开始:你自己
你天生是不完美的-我们常说,人类大多是一个个不同缺点的组成集合。但是,在你了解同事身上的缺点之前,你需要了解自己身上的缺点。我们将要求你反思自己的反应、行为和态度--作为回报,我们希望你能够真正了解如何成为一个更高效、更成功的软件工程师,减少处理与人相关的问题的精力,花更多的时间编写优秀的代码。
本章的关键思想是,软件开发是团队的努力。要在工程团队或任何其他创造性合作中取得成功,你需要围绕谦逊、尊重和信任的核心原则重新定义你的行为
帮我隐藏我的代码
场景:隐藏开源项目的代码
答案:缺乏安全感。人们害怕别人看到和评价他们正在进行的工作。从某种意义上说,缺乏安全感是人性的一部分--没有人喜欢被批评,尤其是那些没有完成的事情。认识到这个主题让我们看到了软件开发中一个更普遍的趋势:缺乏安全实际上是一个更大问题的征兆。
天才的神话
天才的神话是一种趋势,即我们需要将团队的成功归因于一个人/领导者。
乔丹:我们崇拜他,但事实是他并不是一个人赢得每一场篮球比赛的。他真正天才的地方在于他与团队合作的方式。
名人效应是一个重要原因。人类有寻找领导者和榜样的本能,崇拜他们,并试图模仿他们。我们都需要英雄来激发灵感,编程世界也有自己的英雄。"科技名人"已经几乎被神话,我们都想写一些改变世界的东西。
更糟糕的是,你可能会发现自己只解决了分析性问题,而没有解决人的问题。绝大多数工作不需要天才水平的智力,但100%的工作需要最低水平的社交技能。决定你职业生涯成败,更取决于你与他人合作的程度。
隐藏不利
如果你把所有的时间都花在独自工作上,增加了不必要失败的风险,耽误了你的成长潜力。尽管软件开发是一项需要高度集中精力和独处时间的深度智力工作,但你必须权衡协作和审查的价值(以及需求)。
首先,你怎么知道自己是否在正确的轨道上?
提早发现
如果你对世界隐瞒你的伟大想法,并在未完美之前拒绝向任何人展示,那么你就是在进行一场下注巨大的赌博。早期很容易犯基本的设计错误。你冒着重新发明轮子的风险。而且你也失去了协作的好处:注意到你的邻居通过与他人合作而效率有多高?这就是人们在跳入深水区之前将脚趾浸入水中的原因:你需要确保你在做正确的事情,你在做正确的事情,而且以前从未做过。早期失误的可能性很高。你越早征求反馈,这种风险就越低。记住"早失败、快失败、经常失败"这句经得起考验的至理名言。
巴士因子
:团队里因巴士撞倒的多少人,会导致项目失败
除了巴士因子,还有整体进度的问题。人们很容易忘记,独自工作往往是一项艰苦的工作,比人们自认为的慢得多。你独自工作能学到多少?你推进地有多快?google和stack overflow是观点和信息的重要来源,但它们不能替代人的真实体验。与他人一起工作会直接增加工作背后的集体智慧。当你陷入误区时,你需要浪费多少时间才能从困境中解脱?想想如果你有几个同龄人看着你并立即告知你是如何犯错以及如何解决问题,体验会有多么不同。这正是软件工程公司中团队坐在一起(或进行配对编程)的原因。编程很难。软件工程更难。你需要另一双眼睛。
进展速度
尽早获得反馈、尽早进行测试、尽早考虑安全和生产环境。这一切都与开发人员工作流程中的"左移"思想捆绑在一起:我们越早发现问题,修复它的成本就越低。
通过团队合作。人多走得更快,人多走得更远。
在洞穴中工作的人们醒来后发现,虽然他们最初的愿景可能已经实现,但世界已经改变,他们的项目变得无关紧要。
我们仍然认为工程师需要不受打扰的时间来专注于编写代码,但我们认为他们同样需要一个高带宽、低冲突的团队连接。如果你的团队中新人觉得向你提问存在障碍,那就是一个问题:找到正确的平衡是一门艺术。
总之,不要隐藏
一切都是为了团队
社会互动的三大支柱
谦逊 尊重 信任
谦逊、尊重和信任的实践
丢掉自负
尽管谦虚很重要,但这并不意味着你需要做一个受气包:自信没有错。不要表现得像无所不知。最好的是:考虑以"集体的自我为目标":与其担心你个人是否了不起,不如尝试建立一种团队成就感和团队自豪感。
学会给出和接受批评
在专业的软件工程环境中,批评几乎从来不是针对个人的,它通常只是构建更好项目过程的一部分。诀窍是确保你(和你周围的人)理解对某人的创造性产出进行建设性批评和对某人的人身攻击之间的区别。后者是无用的。前者可以(也应该)提供帮助并指导如何改进。而且,最重要的是,它充满了尊重:给予建设性批评的人真正关心对方,希望他们提升自己或工作。学会尊重同龄人,礼貌地给出建设性的批评。如果你真的尊重别人-这是一种通过大量实践获得的技能。
另一方面,你也需要学会接受批评。这意味着不仅要对你的技能保持谦虚,还要相信对方心里有你的最佳利益(以及你项目的利益!),而不是真的认为你是个白痴。编程是一项与其他技能一样的技能:它随着实践而提高。
快速失败并迭代
失败是一种选择。我们普遍认为,如果你没有遇到过失败,你就没有足够的创新或承担足够的风险的能力。失败被视为一个黄金机会,可以在下一次尝试中学习和改进。
无责的事后文化
从错误中学习的关键是通过进行根因分析和撰写"事后总结"来记录你的失败。要格外消息,确保"事后总结"文件不只是一份无用的道歉、借口或指责的清单,这不是它的目的。正确事后总结应该总是包含对所学到的内容的解释,以及作为学习经验作为后续的改进落地。然后,确保事后总结可以随时查阅,并确保团队真正贯彻执行所建议的改变。好的故障复盘要让其他人(现在和将来)知道发生了什么,避免重蹈覆辙。不要抹去你的足迹-让它们的道路上照亮给那些追随你的人。
- 事件的简要概述
- 事件的时间线,从发现、调查到解决的过程
- 事件的主要原因
- 影响和损害评估
- 一套立即解决该问题的行动项目(包括执行人)
- 一套防止事件再次发生的行动项目
- 经验教训
学会耐心
接受影响
成为谷歌的
- 在模棱两可中茁壮成长:即使在环境不断变化的情况下,也能处理相互冲突的信息或方向,建立共识,并对问题做出改进
- 重视反馈:谦虚优雅地接受和给出反馈,理解反馈对个人(和团队)发展的价值
- 走出舒适区:能够设定宏伟的目标并去追求,即使有来自他人的抵制或惰性
- 客户第一:对谷歌产品的用户抱有同情和尊重,并追求符合其最佳利益的行动
- 关心团队:对同事抱有同情心和尊重,并积极主动地帮助他们,提高团队凝聚力
- 做正确的事:对自己所做的一切有强烈的主人感;愿意做出困难或不易的决定以保护团队和产品的完整
第三章 知识共享
你的组织对你问题领域的理解比互联网的一些随机的人要好:你的组织应该能解答你的大部分问题。要做到这一点,你需要知道解决问题答案的专家和哪里和传播知识的机制,这就是我们将在本章中探讨。这些机制在范围很广,从完全简单的到系统化,如教程和课程。然而, 最重要的是,你的组织需要一种学习文化,这需要创造一种心理上的安全感,允许人们承认自己缺乏知识。
学习的挑战
- 缺乏安全感:一个环境中,人们不敢再别人面前冒险或犯错,因为他们害怕因此受到惩罚。这通常表现为一种恐惧文化或避免透明的倾向
- 信息孤岛:在一个组织的不同部分发生的知识碎片,这些部分没有相互沟通或使用共享资源。在这样的环境中,每个小组都形成了自己的做事方式。这往往导致以下情况:
- 信息碎片化
- 信息重复
- 信息偏移
- 单点故障(SPOF):当关键信息只能从一个人那里获得时,就会出现瓶颈。这种心态往往导致失败,组员要么全会要么都不会某方面的知识
- 要么全会要么都不会
- 鹦鹉学舌:模仿而不理解。这典型的特征是在不了解其目的的情况下无意识地复制模式或代码,通常是在假设上述代码是处于未知原因而需要的情况
- 闹鬼墓地:人们避免接触或改变的地方,通常在代码中,因为他们担心会出问题。与前面提到的鹦鹉学舌不同,闹鬼墓地的特点是人们因为恐惧和迷信而避免行动。
换句话说:我们没有形成一个单一的全球最大值,而是有一堆的局部最大值。
理念
软件工程可以定义为多人协作开发多版本程序。人是软件工程的核心:代码是重要的产出,但只是构建产品的一小部分。至关重要的是,代码不是凭空出现的,专业知识也不会凭空出现。每个专家都曾经是菜鸟:一个组织的成功取决于其员工的成长和投入。
团队wiki可能更具普遍性,不太适用于个别学习者的情况,而且随着事件的推移,还需要额外的维护成本来,保持信息的相关性和实时性
搭建舞台:心理安全
导师制
导师制使学习正规化并促进学习
大团体的心理安全
团队互动模式
推荐的模式(合作型) | 反模式(对抗型) |
---|---|
基本的问题或错误被引导到正确的方向 | 基本的问题或错误被挑剔,提出问题的人被责备 |
解释的目的是为了帮助提问的人学习 | 解释的目的是为了炫耀自己的知识 |
回应亲切、耐心、乐于助人 | 回应是居高临下、尖酸刻薄、毫无建设性的 |
互动是为寻找解决方案而进行的共同讨论 | 互动是有"赢家"和"输家"的争论 |
这些反模式可能是无意中出现的:有人可能是想帮忙,但却意外地居高临下,不受欢迎。我们发现Recurse执行的社交规则在这里很有帮助:
- 不要假装惊讶
- 不根据事实
- 不开小会
- 不说微妙的谎言
增长你的知识
提问
了解背景
在移除或改变某些东西之前,首先要了解它为什么存在。在改造事物的问题上,不同于使事物变形。
在你了解代码的背景和目的之后,考虑你的改变是否仍然有意义,如果有意义,就继续做;如果没有意义,就为未来的继任者记录下你的理由。
扩展你的问题:向社区提问
获得一对一的帮助是高带宽的,但规模必然有限。而作为一个学习者,要记住每一个细节很困难。帮你未来的自己一个忙:当你从一对一的讨论中学到一些东西时,把它写下来。
技术讲座和课程
- 主题足够复杂
- 主题相对稳定
- 主题得益于有教师回答问题和提供个性化的帮助
- 有足够的需求定期提供课程
第四章 公平工程
偏见是默认的
当工程师不关注不同国籍、民族、种族、性别、年龄、社会经济地位、能力和信仰体系的用户时,即使是最优秀的工程师也会在不经意间让用户失望。这种失败往往是无辜的;所有的人都存在一定的偏见,社会科学家在过去几十年在已经认识到,大多数人都表现出无意识的偏见,强迫和传播存在的刻板印象。无意识的偏见是隐藏的,往往比有意的排斥行为更难改正。即使我们想做正确的事,我们也可能意识不到自己的偏见。同样,我们的组织也必须认识到这种偏见的存在,并努力在员工队伍、产品开发和用户推广中解决这一问题。
案例研究:谷歌在种族包容方面的失误
图像识别算法把黑人识别为”大猩猩”
以下几个原因:
- 图像识别算法取决于是否提供了一个”适当的”(完整的)数据集。送入谷歌图像识别算法的照片数据显然是不完整的。简而言之,这些数据并不代表所有人口
- 谷歌本身过去(现在)也没有很多黑人代表,这影响了设计这种算法和收集这种数据集的主观决定。组织本身无意识的偏见很可能导致更具代表性的产品被搁置
- 谷歌的图像识别目标市场并没有充分包括这种代表性不足的群体
构建多元化能力
卓越的工程师的一个标志是能够理解产品对不同的人群的好处和坏处。工程师应该有技术能力,但他们也应该有敏锐的判断力,知道什么时候该造什么,什么时候不该造。判断力包括建立识别和拒绝那些导致不良结果的功能或产品的能力。这是一个崇高而艰难的目标,因为要成为一名出色的工程师,需要有大量的个人主义。然而,想要成功,我们必须扩大我们的关注范围,关注我们当前用户之外的未来的用户。
第五章 如何领导团队
经理是人的领导,而技术负责人则领导技术工作
经理和技术负责人
工程经理
技术主管经理
积极模式
-
丢掉自负:信任你的团队
-
管好自己
-
成为催化剂
-
消除障碍:有一些障碍,虽然对你的团队来说几乎不可能逾越,但对你来说却很容易处理,帮助你的团队了解您乐于(并且能够)帮助解决这些障碍是非常有价值的
-
成为一名教师和导师
-
制定明确的目标
-
以诚待人
-
追踪幸福感
-
意想不到的问题
-
人如植物(所需的阳光、水分、土壤肥力要求都不相同)
第六章 规模优先
始终保持决断力,始终保持离开,始终保持扩张
始终保持决断力
更好地权衡。作为一个领导,你需要决定你的团队每周都做什么。有时权衡很明显(如果我们做这个项目,那另一个项目可能会有延期),还有的时候这些权衡会有不可预料的结果,回过头来会反咬一口。
在最高层,你的工作是作为一个领导-一个小团队或一个更大的组织-引导人们解决棘手的、模糊的问题。模糊意味着这个问题没有显而易见的解法,甚至可能没有解法。另一方面这个问题需要被探索、指引、摸爬滚打到一个可控的状态。如果把写代码比作砍树的话,你作为一个领导的工作就是”拨开树木见森林”,找到穿越森林的路径,指引工程师找到最重要的树。首先,你需要找到专家;然后识别权衡;然后在解决方案上反复地决定并迭代。
找到盲点
当你初次接触一个问题时,通常你会发现有很多人已经在这个领域摸爬滚打很多年了。这些家伙在这个领域呆了很久,以至于他们好像戴着眼罩-他们无法”拨云见日”。对于这个问题(或解决方案),他们会做一系列的假设,但从不会去重新认识这个问题本身。他们会说,”我们一直是这样做的”,他们已经失去了思考问题现状的能力。有时,你会发现一些奇怪的应对机制或合理化建议,这些都是为了证明现状的合理性而演变的。这就是你的优势所在-你有一双新的眼睛。你可以看到这些盲点,提出问题,然后考虑新的策略。(当然,对问题不熟悉并不是好领导的要求,但它往往是一种优势。)
确定关键的权衡要素
没有一劳永逸的解决方案,只有当下的最佳答案,而且几乎肯定涉及到在某些方向上的权衡。你的工作是指出这些权衡,向大家解释,然后帮助决定如何取舍
做决定,然后反复迭代
在你了解这些权衡以及它们如何运作之后,你就被赋能了。这个月,你能利用这个信息来做最佳的决定,然后下个月你可能需要重新评估和权衡-这是一个反复迭代的过程。
不过这里也有风险。如果你不给你的分析过程定一个边界的话,你的团队容易陷到寻找”最完美的解决方法”的漩涡中,这样使团队进入所谓的”分析瘫痪”的状态中,你需要使你的团队习惯于这个迭代过程。一种方法是每次把这个变更和风险降低,然后尝试给团队成员解释”我们将尝试这个方案看看效果,然后下个月我们可能撤销这个变更或做出不同的决定”,来使他们冷静下来。这使团队成员变得敏捷,并能从他们的选择中得到成长并进度
好(质量),快(延迟),便宜(容量)只能选两个
两方面开销:
- 第一个开销是对用户:更好的质量意味着需要更多的工作来生成这些数据,也就意味着更多的延迟
- 第二个开销是google本身:更好的质量意味着需要更多的工作来生成这些数据,这将消耗我们更多的服务器CPU时间,也就是我们说的”服务容量”。
始终保持离开
你的任务不仅仅是解决边界不清晰的问题,而是还要引导你的组织在没有你在场的情况下自己解决问题。如果你能做到这点,将释放一部分精力去解决新的问题(或去管理新的组织),在你身后留下一个个能自给自足的团队
划分问题域
搜索延迟
- 定位延迟的现象
- 挖掘延迟的根本原因
与此同时仍有数千名工程师增加系统的复杂度和搜索结果的”质量”,使对系统延迟的优化刚上线就被抵消掉了。因此我们还需要人关注一个平行的问题域,从一开始就防止增加延迟的变更。
委托子任务
你是唯一一个能解决这个问题的人吗?除非这件事真的迫在眉睫,你要硬着头皮把这件工作指派给一个你觉得能够完成,但可能会花费更多时间的人,并且你需要给与适当的指导。你需要创造机会来让你的团队成长;他们需要学着”升级”,然后自己解决这个问题,这样你就不在关键路径上(约束点 布伦特)了。
有什么事情是除了我以为团队里的其他人做不了的?
确保你的领导能够知道你的团队做的怎么样,以及确保在公司规模较大时团队不与公司脱节。但通常最常见也是最重要的答案是:”透过树木见森林。”换句话说,你能够制定一个高层次的战略。你的战略应该不仅包含技术战略,还应该包含组织战略。你的构建一个关于如何解决边界不清晰的问题,以及如何让团队能够长久地解决问题的蓝图。你持续性地在森林中规划砍树的路线,而把砍树的具体问题交给别人。
对于不足的地方反复迭代
管理的意义:95%是观察和倾听,5%是在适当的位置做关键性的调整
重要和紧急
当一个团队遇到苦难的问题,往往会显现出一个标准的解决模型-一个特殊的解决模型
- 分析:首先,你收到一个问题,然后开始尝试解决它。你找出相关的盲点,找到所有权衡点,然后为如何解决他们在团队内达成共识
- 挣扎:你开始着手工作,无论你的团队是否已经准备好。你准备好了迎接失败、重试和反复迭代。从这点上讲,你的工作就像。鼓励你手下的团队和专家们坐下来整理观点,然后仔细倾听,制定全局战略,哪怕最开始你不得不”编造一个战略”
- 前进:终于你的团队开始把事情搞清楚了,你做的决策越来越明智,问题也有了实质性的进展。士气得到了鼓舞。你开始反复迭代权衡,组织开始自驱地解决这个问题。干得漂亮!
- 奖励:你的上级把你叫到一旁然后祝贺你的成功。然后给了你一个新的问题去解决。是的:对于成功的奖励往往是更多的工作和更多的责任。通常是一个类似的,或者关联的问题,但是同样困难。
所以现在你陷入了困境。你呗分配了一个新的问题,但通常并没有更多的人力。这意味着原来的问题仍需要被解决,但只有一半的人力和一半的时间。我们把这最后一步成为压缩阶段:你需要处理所有你正在处理的事情,而且需要把它的规模压缩到原来的一半。
当你在管理岗工作后,你可能慢慢察觉到了,你的工作模式变得不可预测,天天都在救火。你的工作变得被动,更多的是响应别人的诉求。你的岗位越高,这个现象越明显。你成了一堆代号的最终负责人。你的所有通信手段-邮件、聊天室、会议开始让你感觉就像是你时间和精力的”ddos”。事实上,如果你不留心,最终你将花费你的全部时间在被动响应别人的需求上。
美国总统艾森豪威尔:我有两类问题,紧急的问题和重要的问题。紧急的问题并不重要,重要的问题从不紧急。
这两者直接的关系时威胁你作为领导的工作效率的最大的危险之一。如果你放入自己变成纯被动响应式的工作模式,你将会花费你全部的时间和精力解决紧急的事,但是这些东西在宏观层面几乎都是不重要的。
一定要记住你作为一个领导的工作是要做那些必须由你来做的事,比如规划穿越森林的路线。构建这些”元策略”是非常重要的,但几乎从不紧急。相比起来,回复下一封紧急的邮件永远更简单
那么,怎么才能强迫你自己花更多精力在重要的事情上,而不是紧急的事情上呢?
- 委托
- 安排专注时间
- 找到一个有效的进度跟踪系统
学会丢球
找到20%最重要的,20%最不重要的丢掉它,中间60%的也抛弃,如果中间这堆球中有真正重要的事,它最终无论如何都会回到你这里,然后转换到顶部20%那堆球里。
第七章 测量工程效率
人们提出这个问题越具体,他们就越有可能从这个过程中获益。
然后我们要求他们考虑以下问题:
- 你期望的结果是什么?为什么?
- 如果数据支持你的预期结果,将采取什么行动
- 如果我们得到一个负面的结果,是否会采取适当的行动?
- 谁将决定对结果采取行动,以及他们何时采取行动?
目标/信号/指标
- 目标是一个期望的最终结果。它是根据你希望在高层次上理解的内容来表述的,不应包含对具体测量方法的引用
- 信号是你如何知道你已经实现了最终结果。信号是我们想要衡量的东西,但它们本身可能是不可测量的
- 指标是信号的代表。它是我们实际上可以测量的东西。它可能不是理想的测量,但它是我们认为足够接近的东西
保持可追溯性
第八章 风格指导和规则
规则就像法律。它们不仅仅是建议或提议,而是严格的、强制的法律。因此,规则具有普遍可执行性-不得无视规则除非在需要使用的基础上获得豁免。与规则相反,知道提供帮助建议和最佳实践。指导值得遵循,甚至是高度建议能够遵守,但与规则不同的是,指导通常允许出现一些变化的空间。
创建规则
当定义一组规则时,关键问题不是”我们应该有什么规则?”我们要问的问题是:”我们想要实现的目标是什么?”当我们关注规则将服务的目标时,识别哪些规则支持这个目标,可以更容易地提供有空的规则集。
指导原则
我们需要维持一个对规模和时间都有扩展的工程环境
在这种情况下,我们规则的目标是管理开发环境的负责性,保持代码库的可管理性,同时仍然允许工程师高效地工作。我们在这里做了权衡:帮助我们实现这一目标的大量规则确实意味着我们在限制选择。我们失去了一些灵活性,甚至可能会冒犯某些人,但权威标准所带来的一致性和减少冲突的收益是最重要的
规则必须发挥其作用
一个工程师需要记住多少规则
一致性(命名、格式、结构)
如果惯例已经存在,那么一个组织与外界保持一致通常是一个好主意。
一致性是非常重要的,适应更是关键所在
- 规避风险的规则
- 执行最佳实践的规则
- 确保一致性的规则
第九章 代码审查
代码审查通常是对一个特定的变更是否能被更多的人理解的第一个测试
代码审查的一个最重要的,但被低估的好处是知识共享。大多数作者挑选的审查员都是被审查领域的专家,或者至少在所审查的领域有知识。审查过程允许审查员向作者传授领域知识,允许审查员向作者提供建议、新技术或咨询信息。
第十章 文档
文档的质量、数量和完全缺失
把文档当做代码
将文档像代码一样对待,并将其纳入传统的工程工作流程,使工程师更便捷地编写和维护文档
文档的好处都必然是延后的,它们通常不会给作者带来直接的好处。
文档通常与代码紧密相连,所以应该尽可能地把它当做代码来对待。也就是说,你的文档应该:
- 有需要遵循的内部策略或规则
- 被置于源代码控制之下
- 有明确的所有权,负责维护文档
- 对修改进行审查(并与它所记录的代码一起改变)
- 追踪问题
- 定期评估
- 如有可能,对准确度、新鲜度等方面进行衡量
了解你的受众
写文档犯的最主要的错误之一是只为自己写。
- 经验水平(专家级程序员,或者甚至可能不熟悉语言的初级工程师)
- 领域知识(团队成员或组织中只熟悉API端点的其他工程师)
- 目的(可能需要你的API来完成特定任务并需要快速找到该信息的最终用户)
在某些情况下,不同的受众需要不同的写作风格,但在大多数情况下,技巧是以一种尽可能广泛地适用于不同受众群体的方式进行写作。通常,你需要同时向专家和新手解释一个负责的主题。为有领域知识的专家写作方式可能会让你少走弯路,但你会让新手感到困惑;反之,向新手详细解释一切无疑会让专家感到厌烦
通过保持文档的简短和清晰,你将确保它能让专家和新手都满意
另一个重要的受众区分是基于用户如何使用文档:
- 寻求者,工程师知道他们想要什么,并且想知道他们所看到的是否符合要求。对于这些听众来说,一个关键的教学手段是一致性。如果你为这一群体写参考文档-在一个代码文件内,例如-你希望注释遵循类似的格式,以便受众可以快速扫描引用并查看是否找到所需内容
- 浏览者,可能不知道他们到底想要什么。他们可能对如何实施他们正在使用的东西只有一个模糊的概念。这类读者的关键是清晰。提供概述或介绍(例如,在文件的顶部),解释他们正在查看的代码的用途。确定文档何时不适合受众也很有用(文章的顶部 以声明开头)。
最后,一个重要的受众区分是客户(例如,API的用户)和供应方(例如,项目组的成员)。为一方准备的文件应尽可能与另一方准备的文件分开保存。实施细节对团队成员的维护非常重要;最终用户不需要阅读此类信息。通常,工程师在他们发布的库的参考API中表示设计决策。这种推理更适合于特定文档(设计文档)中,或者充其量是隐藏在接口后面的代码的实现细节中。
文档类型
设计文档、代码注释、操作文档、操作文档、项目页面等。一个文档应该有一个单一的用途。
良好文档得衡量标准
完整性、准确性和清晰性
如何提高文档的质量:关注受众的需求
废弃文档
进行标记“已废弃”
第十一章 测试概述
最简单的测试定义如下:
- 你要测试的单一行为,通常是你要调用的一个方法或API
- 一个特定的输入,你传递给API的一些值
- 可观察的输出或行为
- 受控的环境,如一个单独的隔离过程
当你执行这样的测试时,将输入传给系统并验证输出,你将了解到系统是否期望运行。总的来说,成百上千个简单的测试(通常称为测试套件)可以告诉你整个产品符合预期设计的程度,更重要的是,何时不符合预期设计。
创建和维护一个健康的测试套件需要付出艰辛的努力。随着代码库的增长,测试套件也会增长。它将开始面临诸如不稳定和运行缓慢的挑战。如果不能解决这些问题,测试套件将被削弱。请记住,测试的价值来自工程师对测试的信任。如果测试称为生产力的短板。不断地带来辛劳和不确定性,工程师将失去信任并开始寻求解决办法。一个糟糕的测试套件可能比根本没有测试套件更糟糕。
正在开发的产品和服务将不可避免地经历测试失败。真正使测试过程有效的是如何解决测试失败。允许失败的测试迅速堆积起来,就会破坏他们提供的任何价值,所以一定不要让这种情况发生。在发生故障后几分钟内优先恢复故障测试的团队能够保持较高的信息和快速地故障隔离,从而从他们的测试中获得更多的价值。
总之,一个健康的自动化测试文化鼓励每个人分享编写测试的工作。这种文化也确保了测试的定期运行。最后,也许也是最重要的,它强调快速修复损坏的测试,以保持对测试过程的高度信心。
测试代码的好处
- 更少的调试
- 在变更中增加了信心
- 改进文档
- 简单审查
- 深思熟虑设计
- 快速、高质量的发布
碧昂丝规则
哪些行为或属性需要被测试?
:测试所有你不想破坏的东西
代码覆盖率:如果你有100行代码,你的测试执行了其中的90行,你就有90%的代码覆盖率
第十二章 单元测试
第十三章 测试替代
什么是单元测试中的打桩_土豆西瓜大芝麻的博客-CSDN博客_打桩测试
软件测试页面 打桩,软件测试中 的打桩是什么意思?_Wings电子竞技俱乐部的博客-CSDN博客
测试替代可以使一个函数或对象,它可以在测试中代替真正的实现,测试替代的使用通常被称为模拟(Mock)
可测试性
适用性
仿真度
打桩:将行为赋予一个函数的过程,如果该函数本身没有行为,则你可以为该函数指定要返回的值(即打桩返回值)
打桩是通过模拟框架来完成的,以减少手动创建新的类来硬编码返回值所需的模板
过度使用打桩的危害
测试变得不清晰
测试变得脆弱
测试有效性降低
在打桩的情况下,没有办法确保被打桩的函数表现的像真实实现
第十四章 大型测试
- 一个或多个二进制文件的功能测试
- 浏览器和设备测试
- 性能、负载和压力测试
- 部署配置测试
- 探索性测试:探索性测试是一种手动测试,它的重点不是通过重复已知的测试流来寻找行为回归,而是通过尝试新的用户场景来寻找有问题的行为。训练有素的用户/测试人员通过产品的公共API与产品交互,在系统中寻找新的路径,寻找行为偏离预期或直观行为的路径,或者是否存在安全漏洞。
- A/B对比(回归)测试
- 用户验收测试(UAT)
- 探针和金丝雀分析
- 故障恢复和混沌工程
- 用户评价
测试也会腐烂
第十五章 废弃
所有系统都会老化。虽说软件是一种数字资产,它的字节位本身不会有任何退化。但随着时间的推移,新技术、库技术、语言和其他环境变化,都有可能使现有的系统过时。旧系统需要持续维护、深奥的专业知识,通常需要花费更多的精力,因为它们与周遭的生态略有不同。投入些精力废弃掉过时的系统通常是个不错的选项,让它们无限期地与它的替代者共存通常不是明智的选择。我们将有序迁移并最终移除过时系统的过程称为弃用
我们对”弃用”的讨论始于这样一个基本前提,即代码是一种负债,而不是一种资产。毕竟,如果代码是一种资产,我们为啥还要费心去尝试”弃用”它呢?代码有成本,其中有开发成本,但更多的是维护成本。
系统老旧并不意味着它过时了。
弃用最适合那些明显过时的系统,并且存在提供类似功能的替代品。新系统可能更有效地使用资源,具有更好的安全属性,以更可持续的方式构建,或者只是修复错误。拥有两个系统来完成同一件事似乎不是一个紧迫的问题,但随着时间的推移,维护它们的成本会大幅增加。用户可能需要使用新系统,但仍然依赖于使用过时的系统。
这两个系统可能需要相互连接,需要复杂的转换代码。随着这两个系统的发展,它们可能会相互依赖,从而使最终消除其中任何一个变得更加困难。从长远来看,我们发现让多个系统执行相同的功能也会阻碍新系统的发展,因为它仍然需要与旧的保持兼容性。由于替换系统现在可以更快地发展,因此花费精力移除旧系统会有相关的收益。
代码本身不会带来价值:它提供的功能带来了价值。如果该功能满足用户需求,那么它就是一种资产:实现此功能的代码只是实现该目的的一种手段
为什么弃用这么难
一个系统的用户越多,用户以意外和不可预见的方式使用它的可能性就越大,并且越难”弃用”和删除这样的系统。它们有可能只是”碰巧可用”而不是”绝对可用”。在这种情况下,”弃用”不是简单的行为变更,而是一次大变革-彻底的”弃用”!这种激进的改变可能会这样的系统造成意想不到的影响。
另一个不愿”弃用”的现象是对旧系统的情感依恋。
最后,资助和执行”弃用”工作在政治上可能很困难;为团队配备人员并花时间移除过时的系统会花费大量金钱,而无所作为和让系统在无人看管的情况下缓慢运行的成本不易观察到。很难让利益相关者相信”弃用”工作是值得的,尤其是当它们对新功能开发产生负面影响。
鉴于”弃用”和删除过时软件系统的难度,用户通常更容易就地改进系统,而不是完全替换它。增量并没有完全避免”弃用”过程,但它确实将其分解为更小、更易于管理的块,这些块可以产生增量收益。我们观察到迁移到全新系统的成本非常高,而且成本经常被低估。增量”弃用”工作通过就地重构实现的功能可以保持现有系统允许,同时更容易向用户交付价值。
设计之初便考虑"弃用"
设计系统以使其最终可以被”弃用”的概念在软件工程中可能是激进的,但它在其他工程学科中很常见。以核电站为例,这是一项极其复杂的工程。作为核电站设计的一部分,必须考虑到其在服务寿命到期后最终退役,甚至为此分配资金。当工程师知道它最终需要退役时,核电站建设中的许多设计,将会随之改变。
不幸的是,软件系统很少经过精心设计。许多软件工程师更热心于构建和启动新系统,而不是维护现有系统。许多公司的企业文化都强调快速构建和交付新产品,这通常会阻碍从一开始就考虑”弃用”的设计,尽管普遍认为软件工程师是数据驱动的自动机,但在心理上很难为我们辛勤工作的创造物的最终消亡做计划。
软件系统存在后,剩下的唯一选择是支持它,小心地”弃用”它。一家大公司在考虑删除旧项目时需要再仔细地考虑对其投资组合和声誉的影响。
弃用的种类
建议和强制
建议性弃用
我们希望客户迁移,但不强迫他们做
收益不能简单地递增,它们必须具有变革性
强制性弃用
为了强制性"弃用"真正起作用,需要有一个强制执行的时间表。并以警告的形式通知到需要执行迁移的客户团队。没有这种能力,客户团队很容易忽略"弃用"工作,而转而支持其他更紧迫的工作。
同时,若没有安排人员协助,可能会给客户团队带来刻薄的印象,这通常会影响迁移的进度。客户只是将它视为一项没有资金的任务,要求他们搁置自己的优先事项,只为保持服务运行而迁移。这会在两个团队间产生摩擦,故此,我们建议安排人员进行协助迁移。这会在两个团队间产生摩擦,故此,我们建议安排人员进行协助迁移。
弃用警告
管理弃用的流程
避免弃用项目被重新弃用
第十六章 版本控制和分支管理
vcs是一个跟踪文件随时间变化的修订(版本)的系统。vcs维护一些关于被管理的文件集的元数据,文件和元数据的副本统称为版本库(简称repo)
开发本质上是一个分支和合并的过程,无论是在多个开卡人员之间还是在不同时间点的单个开发人员之间进行协调
什么是签入/签出_Tonq_csdn的博客-CSDN博客_签入签出
签出(check out from repository to local workspace)即是将资源下载到本地,进行修改,相当于给资源上锁
签入(check in to repository from local workspace)即是在完成对资源的修改并提交后,将其上传到远程资源仓库,相当于给资源解锁
第十七章 代码搜索(略)
第十八章 构建系统,构建理念(略)
第十九章 体验:谷歌的代码审查工具
典型的审查步骤如下:
- 创建一个变更
- 要求审查
- 评论
- 修复变更并回复评论
- 变更批准
- 提交变更
第二十章 静态分析
静态分析是指通过程序分析源代码来发现潜在的问题,例如bug、反模式和其他无需执行程序就能发现的问题。
第二十一章 依赖管理
管理我们无法控制的库、包和依赖关系的网络
第二十二章 大规模变更(略)
第二十三章 持续集成
- 本地开发的编辑-编译-调试回路
- 在提交前向代码修改者提供自动测试结果
- 两个项目变更之间的集成错误,在两个项目一起提交和测试后检测(即提交后)
- 当上游服务部署其最新变化时,我们的项目和上游微服务的依赖关系之间的不兼容,由我们临时环境中的QA测试员发现
- 在外部用户之前使用功能的内部用户的错误报告
- 外部用户或媒体的错误故障报告
第二十四章 持续交付
教育家有一种说法,没有一个教案能在第一次与学生接触后幸存下来。同样,没有一款软件在第一次发布时是完美的,唯一的保证就是你必须更新它。迅速地软件产品的长期生命周期包括快速探索新想法、快速响应环境变化或用户问题,以及实现大规模开发速度。
任何组织长期成功的关键始终在于其能够尽快将想法付诸实施并交到用户手中,并对他们的反馈做出快速反应。"任何软件工程的最大风险是,你最终建立的东西并不实用,你最终建立的东西并不实用。你越早、月频繁地将工作中的软件展现在真正的用户面前,你就能越快地得到反馈,发现它到底有多大价值"
在交付用户价值之前进行很长时间的工作是高风险的高成本的,甚至可能会消耗士气,更快地适应不断变化的市场。
理想情况下,一个版本和另一个版本之间的变化非常少。
客户不喜欢频繁的更新,而且必须为数据成本和中断付费
客户市场的多样性不是问题,而是事实
- 如果全面的测试实际上是不可行的,就以代表性的测试为目标
- 分阶段向用户群众慢慢增加的百分比进行发布,可以快速修复问题
- 自动的A/B发布允许统计下上有意义的结果来证明一个版本的质量,而无需疲惫的去看仪表盘和做决定
第二十五章 计算即服务
计算是实际允许程序所需的计算能力的简写
后记
常见的软件工程问题的解决方案并不总是隐藏在显而易见的地方-大多数问题需要一定程度的果断敏捷,以确定能够解决当前问题的解决方案,同时也能承受技术系统不可避免的变化
标签:弃用,需要,代码,谷歌,问题,软件工程,测试,团队 From: https://www.cnblogs.com/lhxBlogs/p/17231278.html