《卓有成效的工程师》书评

关于本书

本书英文版《The Effective Engineer》 出版时间是 2015 年,中文版出版时间是 2022 年的 7 月 1 号,是一本刚翻译过来的书。用 Effective 命名的图书很多都是经典之作,管理类的像是《The Effective Executive》、《The 7 Habits of Highly Effective People》,编程类的像《Effective C++》、《Effective Objective-C 2.0》,本书也不例外。

作者 Edmond Lau 早期在 Google 担任搜索质量软件工程师,后作为 Quora 的初创成员之一,领导工程团队致力于用户增长,并为新入职软件工程师制定入职培训和指导计划。他热衷于帮助工程团队建立强大的文化,这本书是他关于如何成为卓有成效的工程师的职业感悟和个人总结。

导读

本书主题是如何成为卓有成效的工程师,即作为工程师如何提供工程效率。对于工程效率给出了一个杠杆率的概念:

杠杆率 = 产出的影响 / 投入的时间

高效的工作方式应该是致力于做高杠杆率的事情。全书主要分为三个部分,第一部分是讲如何聚焦杠杆率高的事情,第二部分为对于高杠杆率的事情如何优化我们执行的方法,第三部分是把时间维度拉长,去投资哪些长期价值高的事情。

聚焦杠杆率

使用杠杆率衡量工作成效

杠杆率还可以用时间的投资回报率(ROI)来类比,追求高杠杆率时,通过公式我们可以得知高杠杆率的事情应该是:

  • 提高产出
  • 降低投入的时间

所以增加工作时间并不符合高杠杆率的目标。当我们感觉可以通过增加时间来完成某件事情是正确的做法时是忽略了时间是有限资源,在这里投入了更多时间意味着我们会在其他事情上可利用的时间被占用了。

除此之外还有一个方式是停止手头的内容,转向杠杆率更高的事情。这三种方式可以引申出三个问题用来评估我们正在进行的工作:

  • 如何增加该工作产生的价值
  • 如何在更短的时间内完成这项工作
  • 是否有其他工作可以在当下创造更多价值

本书的核心思想就是围绕这几个点展开的

定期调整优先级

在调整优先级之前,我们应该先有一个地方能够看到我们当前在做的事情有哪些,这需要一个任务清单去管理我们所做的事情。

有了任务清单最好还有一个工具帮助记录时间的使用,它可以衡量我们在事情安排和时间使用上有哪些不足。番茄工作法一个非常好的尝试,它可以把时间有序的划分成不同的块,这种有起始和结束时间的区间更利于我们意识到时间的存在。比价高效的是一天会有 10-14 个番茄时钟用于处理重要的事情。

当任务列表被填充内容过多时我们应该警惕是否忽略了优先级问题。调整优先级不是一件容易的工作,和大多数技能一样,它需要不断实践。对于什么事更有价值的事情,有两个方面可以考虑:关注直接创造价值的工作,以及关注重要但不紧急的工作。

同时为了保持工作的高效,有几个方法可以用于借鉴:

  • 创建守护者进程。在日历上每周每天或者每周划定一个时间段只专注于工作。
  • 限制同时进行的任务数量。人能够同时处理事情的数量是有限的,多个任务之间的切换本身也是耗费时间的,所以要控制自己同时开展任务的数量。
  • 拖延症是阻碍我们高效的一大问题,可以采用「如果...就...」的方案给自己定一个完成节点。可行的计划类似:如果在下一项工作前有20分钟,我就去审查代码、回复邮件等。

优化执行方法

优化学习方式

在经济学里有复利的概念,一旦利息被加到存款本金中,就会在未来产生复利,复利又会带来更多的利息。

从以上两条对比的复利曲线可以看出,复利是一条指数增长曲线,前期看着会比较慢,但后面的增长速度是非常快的。复利开始的越早,就会越早进入高速增长区。即使利率差比较小,经过漫长的时间之后收益也会产生巨大的差异。

学习和理财一样,前面的学习会为后面的学习打基础,越早对学习方式进行优化,产生的复利效果就会越明显。与之相反,如果我们把时间花在缺乏挑战的工作上时,不是保持原地踏步那么简单,同时我们还在浪费着时间。Palantir 公司的联合创始人斯蒂芬科恩在斯坦福大学的一次客座演讲时强调,「当公司为一份轻松的、毫无挑战的、朝九晚五的工作向你支付薪水时,他们实际上是在付钱让你接受更低的智力增长率。等你认知到智力投资会产生福利效应时,已经为错失长期复利付出巨大的代价。他们没有让你获得一生中最好的机会,而是得到另一件可怕的东西:安于现状。」

投资时间到学习上是最容易产生复利效果的一件事情,我们可以采取这些方式:

  • 培养成长型思维模式。该理论来源于Carol Dweck 在《终身成长》,「固定性思维模式的人坚信,人的能力是先天注定的,后天无法改变。成长型思维模式的人相信,通过后天努力可以培养和提高自己的智力和技能」。
  • 不断对学习方式进行优化,以提升学习速率。
  • 寻找利于学习的工作环境。

关于第三点,如何寻求一个利于学习的工作环境,有这六个因素可以考虑:

  • 快速增长。快速发展的团队和公司能提供大量产生巨大影响的机会。
  • 培训。细致规范的入职流程表明该组织将员工的培训放在首位。Facebook 有一个为期6周的新兵训练营(Bootcamp),新入职的工程师将通过该计划了解公司所用的工具及重点领域,并完成一些初步的实际开发工作。
  • 开放。要追求一种充满好奇心的组织文化,鼓励每个人提出问题,在结合一种开放的文化,让人们积极反馈和分享信息。
  • 节奏。快速迭代的工作环境能提供更短的反馈周期。一个想法从构思到获得批准需要多长时间?快速行动是否体现在公司或工程价值观中。都是可以考虑的方面。
  • 人员。与那些比自己更聪明、更有才华、更有创造力的人一起工作,意味着我们身边有很多潜在的老师和导师、就职业成长和工作幸福感而言,和谁一起工作要比实际做什么工作更为重要。在选择时可以考虑这些问题:面试官看起来比你更聪明吗?你的面试是否演进全面?你想和面试官一起工作吗?
  • 自治。选择工作内容和工作方式的自由驱动着我们的学习能力,可以考虑大家是否拥有选择项目、开展项目的自主权?工程师是否参与产品设计的讨论并能影响产品方向。

这几点因公司和团队而异,不同职业时期各个因素的重要性也会发生变化,但不论怎样,在工作中我们还可以充分利用工作中的资源以提高学习效果:

  • 学习公司里最优秀的工程师编写的核心抽象代码,特别是使用过的核心库,里面会有很多值得学习的东西。
  • 学习也要伴随实践,把学习内容落地到项目中,编写更多代码。
  • 研读内部可以获取的任何技术和学习资料。例如谷歌有大量资深工程师编写的 CodeLab。
  • 掌握你所使用的编程语言,读一两本该语言的优秀著作,再至少掌握一种脚本语言,你可以将其看做快速处理工作任务的瑞士军刀。
  • 请公司里最严格的人审查你的代码
  • 用于学习自己不熟悉的代码

这里可以补充一些我在抖音的工作感受,这里完全符合「利于学习的工作环境」。我所在团队是研发效能,这里有多个方向上还处于探索期,有很多想法可以尝试。这提供了很大的自由空间,我可以选择自己感兴趣的方向去尝试,最大限度地自主设计这些功能,并能得到多方资源的支持。 > > 培训方面字节的机制是很完善的,像 Facebook 内部也叫 Bootcamp,时间跨度为 6 个月(整个试用期)。这里的内容有很多主题,大的有工程师文化讲解,小的有各个技术专题的内部分享。技术分享比较系统,更像是课程,从初阶到高阶,涵盖范围很广,配有完整的知识图谱进行对照。抖音内部每周还会有一个技术分享,邀请其他部门或者内部团队的优秀同学,这些内容又可以沉淀下来扩展整个学习素材库。 > > 人才方面字节最不缺的应该就是聪明、有才华的人了。严格的 CodeReview,有趣的技术方案讨论,和优秀的人一起工作,本身就是一件享受的事。 > > 个人权限也很高,你基本可以看部门内部任意一个代码仓库的源码,你可以向任何人请教某一个技术实现的细节。 > > 再附一个招聘的信息:我们是负责抖音整体研发阶段质量问题的团队,有很多研究方向但苦于人力不够,所以计划在年前再招几位同学,感兴趣的同学可以点击这里了解详情:抖音 iOS 基础技术 - 研发效能方向内推

投资迭代速度

Twitter 平台工程前副总裁 Raffi Krikorian 会不断提醒团队:如果某个任务必须手动做两次以上,那么第三次就去编写一个工具。工具是一个倍增器,它使我们能超越工作时间的限制。

日常工作中,像是本文编辑器、IDE 和浏览器这些高频使用的工具,代码导航、代码搜索、格式化等这些高频的编程习惯,哪怕花费一个小时或者更久去记住一个快捷键或者设计一个自动化功能去提高这方面的效率,长远来看都是非常值得的。有一些行之有效的建议可以采纳:

  • 熟练使用自己喜欢的文本编辑器或 IDE
  • 至少学习一种高级编程语言,一种脚本语言
  • 熟悉 Shell 命令
  • 多用键盘少用鼠标
  • 自动化手动工作流程

关于自动化工作流程,除了脚本本身还可以利用 Alfred 帮助管理任务: > > 在调试一个程序时我需要频繁的检查它的产物,因为这个功能是嵌到另一个项目中的,所以它的层级目录很深,每次误关闭 Finder 都需要点击多次才能找到目的位置。对于快速定位一个目录,可以利用 Alfred 的 Default Results 功能,为这个目录配置一个替身,当我要打开该目录时直接搜索替身名即可。 > > 类似的还有通过路径打开 Finder 对应位置,直接运行一段脚本,通过快捷键用Xcode/VSCode/PyCharm打开指定文件或文件夹等等。都可以配置为 Alfred 的 Workflow。 > > 当原本需要三个或更多步骤才能完成的事情缩短为一个步骤时,这边便捷感会让我们的工作流程更流畅,聚焦与真正要处理的事情上。

通过度量改进目标

Peter Drucker 在《The Effective Executive》中指出「如果你不能衡量它,你就无法改变它」。

以谷歌搜索为例,输入一个查询,可能对应的结果就有数十亿条,算法会计算出将近 200 个指标,然后返回前十条结果。那么如何衡量给出的这十条结果对用户来说是最优的呢?谷歌给出的衡量是用户愉悦感,让用户最舒服的结果就是最好的,衡量愉悦感是用「长点击」测量的,就是当一个人点击了一个搜索结果,没有立即范围搜索页,就表明用户对搜索结果是满意的。好的度量指标能推动我们不断优化工作结果。

有时因为系统复杂性的原因,很难通过单一指标去衡量它的运行状况,这时可以引入监控系统,从多个方面衡量起健康和稳定。

与此同时,还需要确保你的数据是可靠的,「数据都正确」比没有数据更糟糕。特别是数据指标建设早期,我们应该多方便去衡量数据指标的正确性,比如广泛记录日志以备用,尽早检查采集到的数据,通过多种方式计算同一指标,交叉验证数据准确性,尽早分析有问题的数据,以找到指标变动的原因。

工作成果需要借助于度量来改进,同时我们的工作效率也可以引入度量系统。有时数据层面反应的问题,会比事情本身更有价值。 > > 我最近发现了一个宝藏 App:Session,它同时满足了我任务记录、反思、度量这几个需求。我通过查看自己过去半个月的记录情况发现,我最高效的一天是完成 7 个番茄时钟;遇到最多的问题是任务规划不合理导致很多任务超时和中途被打断;第二周的番茄数量相比第一周有所下降。 > > > > 这里完整反应了我对时间的使用情况,并且可以根据这些数据做出针对性调整。

建立反馈循环

在做项目,尤其是大型项目时,花费一小部分精力用来搜集数据,验证目标可行性是非常值得的投入。它们可能会增加我们 10% 的开销,但也有可能提前暴露问题,节省 90% 的精力。这是一种通过搜集尽可能多的信息来预见结果的提前反馈机制。现代项目基本都会引入 DevOps 流程,这也是一种为了加快验证代码效果以建立反馈循环的机制。

除了项目整体,作为个人在项目开发过程中也需要尝试建立与外部的反馈循环。通常我们会遇到这种场景,在实际工作中我们必须独立完成一个项目,它减少了外部沟通成本的成本但却增加了反馈阻力。如果这期间遇到某个实现被卡主,会很让人郁闷。我们可以尝试这些策略用于获取反馈:

  • 尽早并经常提交代码并进行代码审查。
  • 请求严厉的批评者审查代码,通过严厉的审查要求,我们更容易获得反馈。
  • 征求团队成员的反馈,把自己的想法告诉一位成员,想他解释你正在做的事情,有可能我们在解释时就会意识到一些优化点。
  • 编写代码前先展示设计文档,通过设计文档去考虑之后要做的事情有哪些。

提升项目进度把控能力

「这个功能大概多久能做完?」这是每个程序员会经常面临的问题,针对估算,Steve McConnell 甚至写了一本书《软件估算的艺术》。一个好的估算能够对项目的实际情况提供足够清晰的视角,使项目负责人能够就如何控制项目以达到目标做出正确的决定,而一个差的估算,会让自己陷入紧张的情绪和无休止的加班,甚至会引起依赖业务方的连环 Delay。

准确的估算是很难做出的,但我们可以采用一些策略让估算尽可能地贴近真实值:

  • 将项目分解为细粒度的任务。
  • 根据任务需要进行估算,而不是自己或者别人希望花多长时间进行估算。
  • 将估算结果视为概率分布,而不是最佳情况。例如我们可以说「我们有50%的可能在4周后交付该功能,但有 90% 的可能会在8周内交付」。
  • 谨防锚定偏差,这是人的一种潜意识,项目经理可能会给出一个大概的数字,或者在我们不了解完整需求的前提下先给一个大致估期。虽然我们可以再做时间上的修正,但因为有了这个锚定排期,我们很容易向这个值靠近而非真实的估期。
  • 根据历史数据验证估算结果。比如之前你的估期总是低估2 0%,但此次估期你可以在原有基础上再增加20%的时间。
  • 可以就估算结果进行审查,并允许他人质疑估算结果,有些经验更丰富的人可以帮助我们发现估算中的错误或者确认内容。

在项目开始之前还应该为意外留出一些预算(Buffer),因为除了工作我们还会有 bug 修复、面试、团队会议、回复邮件等等。一天有 8 小时用于工作,并不表示我们可以用这 8 个小时完全去做这个项目。

除此之外设定一些具体的项目目标和可度量的里程碑也非常有用。明确我们到底要解决什么,有助于我们确定哪些工作在范围之内,哪些在范围之外。很多时候我们在开发一个功能时会发现一块代码需要重构,且这块重构会不知不觉导致我们投入很多时间。这时关键目标就可以帮助我们确认,这个重构是否值得。

当我们被问及某个项目的进度时我们通常会说完成了 50%、90%。这个估算是很主观很模糊的。一种比较好的方式是为目标建立一些里程碑,作为项目进展的检查点,中间阶段应该是完成了某个功能、达到了什么效果、对指标优化了多少等等。

但项目在进行到某个阶段之后,我们可能意识到已经出现了 Delay 风险,这时大部分人考虑的是加班。对待加班一定要慎重,因为增加工作时长并不意味着一定能赶上发布日期。加班冲刺还很容易产生大量技术债,这些技术债很可能会成为我们以后得麻烦。

尽管加班有种种弊端,可能经过深思熟虑之后,我们确定可以通过一小段时间的加班赶上最终的进度。在开始之前,需要告知每个人都了解进度落后的原因,并制定更切实可行的项目计划和时间表,如果某个阶段发现还是会延误,那么就修改原定任务或者放弃冲刺吧。

投资长期价值

权衡质量与务实

软件质量可以通过严格的 CodeReview、单测、代码覆盖率、自动化检测流程保证,但这些繁琐的流程会导致项目不够敏捷,过于敏捷又不利于控制代码质量。所以软件质量是一个需要权衡的问题,回到杠杆率上,我们应该去关注长期价值,我们追求的是什么。在衡量这两者时,有这些方法可以尝试:

  • 建立可持续的代码审查流程。它可以帮助尽早发现错误,因为考虑到代码会被审查,它还可以增加代码提交者的责任心。
  • 利用抽象控制复杂性。将一些复杂的事情进行封装,抽象一次性解决难题,且解决方案可以多次使用。Google 构建了 Protocol Buffers 以可以扩展的方式对结构化数据进行编码,构建 BigTable 用于存储和管理 PB 级结构化数据,以提高生产力。
  • 引入自动化测试。自动化测试最大的好处就是解放人工测试的成本。编写第一个测试通常是最难的。一旦有了一些好的测试策略、测试模式和测试代码库,将来再编写测试时所需的工作量就会减少。
  • 偿还技术债。Martin Fowler 在《重构》中指出「最常见的问题是开发者失去对技术债的控制,并将未来大部分的开发工作花在支付技术债的巨额利息上。」谷歌会举办「修复日」活动作为偿还技术债的轻量级机制,鼓励软件工程师解决特定主题的技术债问题。与其他问题一样,技术债是否必须偿还也是需要权衡的,如果可以判断出它影响重大就需要尽快处理,如果它影响很小,我们应该将这部分时间用于更重要的事情上。

为团队成长投资

招聘应该是工作中非常重要的一件事情,所以设计一个有效的面试流程是一件高杠杆率的事情。为团队添加一名实力强劲的工程师所带来的额外产出,远远超过你可以做的其他许多投资的产出。

为招聘投资可以从两方面考虑,一个清楚需要招聘什么样的人才,不断迭代优化面试流程。一个是设计一套高质量的入职培训,让新员工更快速的融入公司。

招聘流程优化:

  • 定期开会讨论招聘和面试效果,找到准确评估团队重视技能和素质的方法。
  • 设计具有多层难度分级的面试题。
  • 控制面试节奏以保持高信噪比。
  • 旁听其他团队成员的面试流程,并彼此反馈

Quora 的入职流程:

  • CodeLab。它是一份文档,用于解释产品中的一些核心抽象的设计理念和使用方法,并提供编程练习以帮助验证对这些核心抽象的理解。
  • 入职培训讲座。新员工入职前三周,会有10场入职培训,培训由团队中资深软件工程师担任讲师,会介绍代码库和网站架构,并演示不同开发工具的使用,和工程实践的价值观。
  • 导师制。每个新员工都会配一名导师,以便为他提供更加个性化的培训。
  • 入门任务。新员工会有第一天任务,第一周任务等,用于快速适应工作环境。

另有两点能够体现团队价值的事情:

  • 代码所有权的问题。当一个功能只有你知道时,不要把这当做自己的价值体现,因为你会因此失去处理其他工作的灵活性。在团队中找到可以满足这些需求的其他成员,能让你有更多自由去专注于其他高杠杆的活动。
  • 事后复盘汇聚集体智慧。我们总不可避免的犯一些错误,或者获得一些好的经验。及时复盘可以帮助我们进行事后总结,不管是好的案例还是不好的案例都能从中吸取一些教训。

总结

好的工程师文化能够吸引优秀的人才,不好的工程师文化会导致优秀的工程师离开。关于什么是卓越的工程师文化,它们具有这些特点:

  • 优化迭代速度
  • 坚持不懈地推动自动化
  • 构建正确的软件抽象
  • 通过代码审查来关注高质量代码
  • 在工作中互相尊重
  • 建立代码的共享所有权
  • 对自动化测试投资
  • 提供实验时间,不管是工作时间的20%,还是黑客马拉松
  • 培养一种学习和持续改进的文化
  • 聘用最优秀的人

作为普通员工应该向这样的团队靠近,作为管理者则应该把团队往这个方向塑造。

iOS 摸鱼周报 #71 | One More Thing?iOS 摸鱼周报 #70 | iOS / iPadOS 16.1 公测版 Beta 3 发布,支持老款 iPad 台前调度