小马的世界

读书笔记-软件工程师指南【6-26】总结-生涯学习

下面的内容是我在作为一名程序员入职之前阅读的由Gergely Orosz写的The Software Engineer’s Guidebook。我将将阅读时得到的重要的信息总结成中文以供大家分享。

是什么让最优秀的软件工程师与其他人区分开来?其中一个关键点就是,这些优秀的工程师从未停止学习。他们不仅会学习新的编程语言和技术,还会深入研究全新的、有趣的方法。

以Simon Willison为例,他是Django Web框架的联合创始人,曾担任Eventbrite的架构总监,并在本书出版时是一名独立的软件工程师。Simon从事编程已有20多年,是一位非常高效的工程师,同时他从未停止学习新的方法。在ChatGPT正式发布几个月后,Simon记录了他对大型语言模型的实验以及他从中学到的如何让自己成为更优秀开发者的经验。不仅如此,他还在短短几个月内成为了探索LLM提示注入(prompt injections)领域的专家。

对我来说,Simon是一个从未停止学习的软件工程师的典范。他证明了总会有新的、有趣的东西值得我们去理解和应用。

1. 保持好奇心

无论你对技术和软件工程了解多少,总会有更多的东西值得探索。而体验这一点的一个可靠方式就是提问。

提问

始终要明白你为什么在做某件事,以及事情是如何运作的。不断问“为什么?”和“如何?”直到你得到答案,不要害怕为了找到答案而深入研究。

以下是一些你可以(也应该!)在工作中提出的问题示例:

  • “为什么我们要做这个项目?谁会从中受益?”作为一名软件工程师,你的工作不仅仅是完成任务和写代码,而是为你的团队和业务创造价值。因此,从了解你的工作为何重要、谁会从中受益以及如何受益开始。
  • 举个例子,当你开始处理一个任务时,上面写着“将提交按钮的填充宽度增加4像素”,问问为什么要这样做。可能是因为应用程序存在可访问性问题,老年用户在使用时遇到了困难。在这种情况下,增加填充可能是一个解决方案,但它是最实际的吗?是否只有提交按钮存在这个问题,还是所有按钮都有类似的问题?是否可以用更智能的方法解决整个应用程序中的一类可访问性问题?
  • “为什么现在它能正常工作了?发生了什么变化?”有时在尝试解决一个bug时,问题似乎“自己”解决了。这是个好消息,对吧?你可以继续工作并将问题标记为“对我来说正常工作”、“无法重现”或“可能已经修复”。

但有了质疑的心态,你不会止步于此。毕竟,你并不知道真正发生了什么。也许问题仍然存在,只是影响了某些用户、某些配置或某些地区。也许是另一个无关的代码更改解决了这个问题?也许这是一个非确定性问题?

戴上你的侦探帽,调查真正发生了什么。没有所谓“自己修复”的bug;问题的发生总有原因。如果你提出问题并进行调查,你会发现真正发生了什么,并在此过程中成为一名更优秀的软件工程师。因为你会学到一些关于追踪这些问题的宝贵知识,了解计算机系统的工作原理以及你之前未意识到的边界情况。

  • “我们还能使用哪些替代方案?”在我职业生涯的早期,当我在一家软件咨询公司工作时,我被分配到一个项目,为客户构建一个简单的CRUD(创建/更新/删除)业务应用程序,用于管理业务记录。一位为项目提供建议的高级软件工程师建议我使用内部的对象关系映射工具(ORM)来存储和检索该应用程序的数据。”

我当时不确定为什么要这样做,但没有提问,并假设高级工程师最了解情况。我使用这个封装工具构建了应用程序。然而,当项目交接时,我注意到一个令人担忧的问题:当记录数量超过50条时,应用程序变得非常慢。插入或更新一条记录需要5秒,然后是10秒,甚至更久。问题在于这个自定义的ORM工具在处理哪怕是小型数据集时表现也非常糟糕。

我向高级工程师询问了我们可以使用哪些替代方案,以及这些替代方案的意义。在我们讨论选项时,很明显,放弃这个ORM工具或者使用一个成熟的、没有已知性能问题的ORM工具会更为明智。最终,我重写了应用程序的数据层,采用了一个流行的开源ORM工具。

如果我当时质疑了最初的方法并评估了替代方案,我本可以节省许多时间。”

其他值得问的问题:

  • “这个解决方案具体是如何工作的?”深入研究,直到你完全理解细节为止。
  • “这个组件底层是如何运作的?”在现代软件工程中,库或框架通常完成了构建应用程序的大部分工作。然而,如果你不了解工具、库或框架的底层工作原理,你就是在亏待自己,同时也错过了一个学习的机会。
  • “如果我们从零开始构建自己的解决方案,这意味着什么?”在构建项目时,你可以选择使用现有组件、供应商,或者自己开发。我认为询问自己构建解决方案的意义是明智的,这并不是因为你一定要这么做,而是因为这样做可能会揭示出组件或供应商需要完成的功能。”

保持好奇心并谦逊

我观察到,那些终身学习者通常具有谦逊和平易近人的特质,即使他们已经是资深人士。他们对学习充满渴望,并且在发现新事实和信息时,能够灵活地改变自己的观点。

一个例子是Kent Beck,他是“极限编程”(Extreme Programming)的创始人,也是测试驱动开发(TDD)的主要倡导者。他在2011年加入Facebook,当时他已经50岁,并且认为自己对技术领域已经见多识广。他在完成入职培训后接受《软件工程日报》采访时谈到了对Facebook的印象:

“我看到这个地方,太疯狂了。看起来像是一个小丑表演,但事情却运行得非常好。他们并没有按照我书中的方法做事。(……)

在我脑海深处,有一个谜团:这只‘大黄蜂’。理论上,这个过程应该是一场灾难。但实际上,它在同时完成两件事方面表现得非常好:扩展和探索。”

在加入后,Kent在一次黑客马拉松中举办了一堂关于测试驱动开发的课程。他的同事并没有使用TDD,所以他以为他们会来学习。然而,没有人参加Kent的TDD课程。与此同时,阿根廷探戈舞课程却超额报名,还有等待名单。

Kent在业内成就斐然,备受尊敬,TDD也被广泛认为是软件开发的最佳实践之一,但Facebook却没有使用它。换作其他类型的软件工程师,可能会试图强行在公司推行TDD,或者选择离开去别的地方。但Kent Beck没有这样做:他采取了谦逊和好奇的态度,开始了解Facebook是如何在没有TDD的情况下运营世界上最大的网站之一的。

这种方法让他认识到Facebook如何优先快速推进,同时依赖先进的实验和回滚系统;以及在Facebook的情况下,没有测试的快速推进是如何成为可能的。Kent Beck在Facebook度过了一段充实的职业生涯,他在工作场所受到尊重,并指导了数百名工程师。如果他没有保持谦逊和好奇,这一切都不会发生。

2. 持续学习

结对编程和旁听学习

与另一位工程师合作解决问题,比如通过结对编程或结对设计解决方案,是解决问题并从同事身上学习的优秀方式。

结对编程是一种少数资深软件工程师极力推崇的实践,因为他们看到了这种方式的高效性。然而,大多数工程师并不这样做,许多人甚至从未尝试过。”

当你在某个问题上卡住时,考虑邀请一位队友与你一起结对解决问题。这就是结对编程的全部意义!

一旦你熟悉了结对编程的过程,你可以主动提出与那些你注意到卡住的工程师一起结对。

旁听是指参与一项活动、会议或事件,这些通常是你无法参与的。例如,工作中有许多会议你没有被邀请参加,但如果你感到好奇,认为参加其中一个会议会有所收获,那么可以询问被邀请的人是否可以带你一起旁听。只需记住,旁听意味着观察,而不是发表意见。

适合旁听的场景包括:

  • 如果你在面试方面经验不足,可以请求旁听一次面试,观察它是如何进行的。
  • 软件工程师没有直接发言权的会议,比如产品经理的战略会议,或者团队成员参加的面向总监的事故回顾会议。
  • 其他团队的定期活动。例如,作为技术负责人参加其他团队的每周更新会议,观察它是如何运行的以及团队的动态如何。

“当你听说一个有趣的会议或活动,但没有被邀请参加,而你认为这是一个学习的机会时,向组织者询问是否可以旁听。最坏的结果不过是被拒绝!”

导师指导

拥有一位导师向其学习,同时指导他人,是持续学习和成长的绝佳方式。导师指导是一种双向关系,导师在分享经验的同时也能从学员身上学到东西。

我们将在第三部分‘协作与团队合作’中详细探讨导师指导。

自我学习

在技术领域中,自我学习是一项必备技能,它能够让你更具自主性,并在遇到不懂的事情时,通过学习来解决问题!

在软件工程领域,在线学习资源丰富,涵盖语言、框架和方法,包括:

  • 语言、框架和库的参考文档。这些资源通常比较‘干’,但通常是最为最新的资料。
  • 教育资源,如文章、书籍、视频和课程。这些资源数量不断增长,包括免费和付费的内容。对于越是小众的领域,付费资源往往越有帮助。这些资源的教育价值通常能让它们物有所值。此外,你的雇主可能会有学习与发展预算,这意味着你无需自己花钱。”
  • 支持与问答网站,比如StackOverflow、Reddit、Discord服务器以及其他编程社区。
  • 人工智能工具。大型语言模型(LLMs)可以作为有用的工具,提供理解和解释编程上下文的指引。但不要忘记,这些工具有时会编造内容,因此不要盲目相信它们!
  • 实践学习。我将最有效的方式留到了最后:通过构建你想学习的技术或方法来学习!例如,如果你想学习一门新语言,比如Go,可以用它构建一个服务或重写一个现有服务。没有什么比亲自编写代码和调试问题更能让你获得第一手经验了。上述资源可以在这个过程中为你提供帮助。

随着你在某个领域经验的积累,你的学习过程会发生变化:

  • 当你是某个领域的新手时,比如TypeScript语言,有指导是非常有帮助的。这种指导可能来自有明确意见的资源,或者来自一位担任向导的资深工程师。
  • 在中级阶段,更有用的是自己探索某个领域并寻找专家资源。例如,如果你有TypeScript的经验,可以尝试它的高级特性,比如联合类型、keyof关键字,以及RequiredPartial工具类型。此时,查阅详细介绍高级语言特性及其底层实现的高级资源会非常有帮助。
  • 当你成为某个领域的专家时,通过教授他人和‘突破边界’来实现成长。例如,以TypeScript为例,向工程师讲解这门语言的工作原理,并为他们创建书面或录制的资源,是加深你对它理解的好方法。承担高级项目,比如为TypeScript编写一个编译器,是一个能够同时挑战你对语言理解和编译器工作原理的项目。参与用该语言编写的广泛使用的项目是另一种方式。”

分享你的学习成果

深入学习某件事情的一个好方法就是教授它,因为这要求你:

  • 理解它的工作原理
  • 将其分解为足够简单的术语
  • 回答问题,包括那些你之前没有想到的问题

以我自己的经验来说,当我解释概念、框架或系统时,我对它们的理解会加深,而且我经常会发现自己知识中的空白,这意味着我需要更好地理解它们。当然,教授的主要好处是提升他人,但额外的好处是,这也让你在该主题上成为更大的专家。

这里有一个有用的小窍门:如果你真的想强迫自己学好某件事情,做一次演讲或举办一场关于它的分享会。我在Uber工作时,为了更好地理解它的几十个系统是如何协同工作的(这些系统由大约10个团队管理),我主动提出为新员工准备一场关于这些系统的入职培训演讲,内容包括每个系统的功能以及它们如何协作。为此,我需要拼凑出各部分,并深入理解系统的每个部分。”

积累知识

Simon Willison 对积累知识的价值有一个有趣的观察。他告诉我:

‘我认为,在技术领域取得成功的一个重要部分就是不断地积累你知道如何做的新事物,然后寻找机会将它们结合并应用起来。’

这就是为什么涉足与你“主要”专业领域不太相关的领域是有益的。例如,如果你是一名前端工程师,可以尝试研究一个不同的平台,比如后端或嵌入式系统。尝试使用新的技术,比如大型语言模型或机器学习。学习一个不同的领域并“积累”这些知识。它现在可能没用,但以后可能派上用场!”

你的学习方式会随着时间而改变

学习的方法是否有效,取决于你是行业的新手还是某些领域的专家。没有一种方法适合所有人,但以下是我观察到的一些现象:

  • 在一个新领域起步时,有人指导你,或者你按照教程学习,通常更高效。
  • 当你对某个领域有深厚的兴趣时,自主学习通常效果很好。
  • 需要解决问题可能是一种非常强大的动力,促使你以最有效的方式弄清楚某项技术是如何工作的(或者为什么不起作用!)

我曾经在不同的学习媒介之间切换过。从通过书籍学习一门新语言或技术,到视频教程,再到参考文档,然后又回到书籍,现在则使用AI工具。我猜测我的偏好还会随着时间继续改变。”

不仅仅是你的个人偏好会改变。团队的动态也会在很大程度上影响你从同事知识中学习的方式。如果他们愿意帮助你,并且在你需要取得进展的领域有相关经验,那么通过这种方式你通常可以学得更快。

接受你的偏好和学习方法会随着时间改变的事实,不要害怕尝试新的方法!”

3. 持续挑战自己

当你在探索和理解某项新技术或系统,或者适应一个新的团队或工作环境时,总会有一个学习曲线。一开始,这条曲线很陡峭,因为你需要吸收和整理大量的新信息。随着时间的推移,当你成为专家时,这条曲线会逐渐趋于平缓。将这一过程画出来:

学习过程分为三个不同的阶段。以学习使用一个新框架为例:

  • 入门/初学者阶段:你刚开始接触这个框架,通过阅读代码或使用它进行简单的更改来入门。最初,你会在基础知识上遇到困难,但你会很快克服这些障碍;至少,相较于第二和第三阶段,这个过程会更快。
  • 熟练阶段:你掌握了基础知识,并在使用和了解框架的过程中,开始发现和使用更多高级功能。起初,你的进步和之前一样迅速,但随着时间推移,这种速度会减慢。你开始深入研究框架的底层原理,调试复杂的错误,最终达到一个几乎没有什么会让你感到意外的地步。
  • 专家阶段:在长时间使用框架后,你成为了专家。几乎没有什么新东西可学了。当然,如果框架从未发生变化,这将是真的,但事实并非如此!当框架的新版本发布时,你的水平会回落到“熟练”阶段。”

当你达到‘专家’水平时,学习曲线会趋于平缓。迟早,你会在任何技术、团队或公司中达到这个阶段。唯一让你无法真正感到自己是专家的地方,是那些快速变化的环境,比如一个迅速变化的行业前沿,或者一个处于高速增长的初创公司。

尽管如此,大多数工程师确实会达到‘专家’阶段,当你到达这一阶段时,你会感受到它。那么,接下来呢?有以下几种选择:”

  • 什么都不做。保持你当前的专家身份,享受成为该技术领域的权威人物。跟上偶尔的变化,比如新版本,并深入理解这些变化。
  • 多教导他人。保持你当前的专家身份,同时帮助他人提升。通过教学,你可以继续自己的学习。
  • 与行业接轨。如果你在公司内部是某一领域的知名专家,你可能会通过接触公司外部学到更多。与行业领军人物和活动接触,发表你的学习成果,是实现这一目标的方式。当然,你可能需要一些来自经理和雇主的支持来追求这些。然而,这么做可以让你自己和你的公司在某项技术领域中成为行业内的知名人物。
  • 挑战新技术。例如,如果你是一名使用Go构建后端服务的专家,可以尝试学习新的语言或使用新的框架。”

在新的领域或平台中挑战自己。更大胆的做法是,不仅仅改变技术,还改变你的领域或平台。例如,如果你觉得自己是一名后端工程师的专家,可以尝试探索前端,深入研究机器学习和人工智能,或者其他你是初学者的领域。

当你是专家时,接下来要做什么没有对错之分。我建议,如果你是专家,最好主动决定你想做什么,而不是随波逐流。你是满足于保持专家身份,还是想更多地教导他人,亦或是时候迎接新的挑战,感受学习新事物的刺激?

4. 跟上行业发展

技术领域唯一不变的就是变化。技术在不断变化,新语言和框架不断涌现,现有技术也在持续改进;方法和平台亦是如此。有时变化需要几年时间,而有时仅仅几个月就会发生。

一个在几个月内迅速传播的变化例子是大型语言模型。ChatGPT 于 2022 年 11 月公开上线,仅仅 3 个月后就拥有了 1 亿月活用户,其中包括许多利用它更高效编写代码的软件工程师。

跟上变化的方法包括:

  • 通过工作。选择在使用前沿技术的“现代”公司工作是保持技术更新的一个原因。只需上班,你就能与时俱进!如果你的团队使用现代语言的最新版本和流行框架的最新稳定版本,那么通过你的工作,你自然会保持技术的更新。这也是拥有现代技术栈的公司对开发者具有吸引力的原因之一。
  • 关注科技新闻。阅读新闻简报和网站,收听播客,观看总结技术领域及你所在领域最新动态的视频。例如,如果你是一名在工作中使用 Go 和 Rust 的后端工程师,可以寻找这些细分领域的资源。为工程师提供最新资讯的出版物不胜枚举。你还可以使用像 Feedly 这样的聚合工具,订阅涵盖有趣话题的多个网站,并时不时阅读相关的文章。
  • 构建副项目。在个人时间进行的副项目以及工作中的原型开发,是避免落后于技术的两种最有效方式。如果你在工作中没有机会做这些事情,可以考虑在工作之外花时间构建概念验证项目。尝试新的框架、语言、平台和方法。
  • 在工作中尝试技术!即使在工作中,也寻找机会尝试新技术或有趣的方法,即便它们与日常工作没有直接关系。你可以用一直想尝试的框架为自己或团队开发一个简单的工具原型。或者,你可以玩转一项新技术,然后与团队一起举办一场午餐分享会,交流你的学习成果。

5. 给自己放个假

本章的建议可能会让人觉得你需要在生活中的每分每秒都在学习。事实完全不是这样!

定期学习新东西确实很好,但在你的工作过程中,自然会有一些时候无法做到这一点,或者你缺乏意志力或机会去学习。

你需要给自己放个假,就像运动员不会每天训练,也不会每次都以全强度训练一样。显然,技术行业发展迅速,因此如果你没有跟上最新动态,感到错失恐惧症(FOMO)是可以理解的。然而,我的观点是,为了不落后而持续学习的做法,可能会导致倦怠。

有时候,你需要让自己从一切中抽离出来休息一下。不要因为这样做而感到内疚!