首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

谷歌首席软件工程师:如何设计大型应用程序

这是我在JSConf Hawaiʻi大会上的演讲笔录。

嘿,大家好!我叫Malte,是谷歌的首席软件工程师,今天我想谈谈如何设计大型应用程序。这是我两年前在JSConf Australia的一次演讲的续集。就像上次一样,我的演讲将立足于软件工程师的职业发展。我想,在座的很多人会称自己为高级工程师;或者,如果你还没有做到,你渴望成为其中的一员。

我将描述什么是高级工程师,如果有人到我这里来说,“嘿,Malte,你熟悉这个领域的这个项目吗?”,我会说,“是的,我确实知道怎么做。我不需要别人的帮助。”

在上一次演讲中,我探讨了如何在软件工程中超越这个级别。不再只是关于你自己,而是你的技艺开始影响其他工程师。你会说,“我可以预见到别人怎么做,然后设计相应地API。”

在这次演讲中,我还想再进一步。我想达到这样一个境界,我可以说:“我可以为大量的工程师设计软件,这样可以增加他们开发出优秀软件的概率。”

这句话有三个关键词。

  • 首先是“大量的工程师”。如果你在一家初创公司或三人公司工作,那么这个演讲可能会显得多余和无聊。但是我想你们中的许多人可能在保险公司、银行、机构、大型科技公司等工作——这些公司足够大,你有一群人,多个团队,你需要协调工作。
  • 接下来我要讲的是“概率”。这里没有确定性。你只能试着把事情安排好,这样它们才有可能会奏效。但没人能保证。没有什么灵丹妙药。
  • 第三个关键词是“优秀”。这不是关于产品管理的演讲。这也不是一个能帮你写出正确程序的演讲,但它能帮你写好程序。我的意思是它具有可维护性、高性能、低Bug密度、按时交付等等。

我还想澄清一件事:我会经常提到框架和软件基础设施这两个词。我指是帮助我们构建更好的软件的软件。在这次演讲中,我希望你们所有人,都能把你们自己当成是你们公司中一个负责定义人们如何编写软件以及构建用来编写软件的基础设施的人。当我说你构建一个框架时,我并不是说你一定要做出自己的React或Angular。相反,我认为,当你有一系列团队时,你想要标准化他们构建应用程序的方法。所以,你提供了一个严格的框架。

再说一下,把你自己想象成负责团队软件基础设施的人。这是一个关于如何让你在工作中取得成功的演讲。我将分三章来讲这个问题。

  1. 第一章我称之为了解不确定性的程度。
  2. ……然后我们将学习如何解决所有已知的问题。
  3. 最后,我们将学习如何部署变更。

了解不确定性的程度

我们能在多大程度上确定未来需要解决的问题的类型?这个问题绝对是软件工程的关键,我要讲的这种技术可以帮助我们了解我们对事物的了解程度。

海森堡不确定性原理的发现者维尔纳·海森堡的照片(图片来源

我们要做的是试着回答下面这一系列的问题:

  • 用户在现有的基础设施中遇到了什么问题?他们是如何解决的?
  • 从广义上讲,人们将使用你的工作成果构建什么样的应用程序?
  • 影响行业的趋势是什么?这些趋势对人们将来可能编写的软件有什么影响?

所有这些听起来可能有点像预言,但我认为,借助一点经验,有很多场景下,你都可以做出很好的回答。这个活动的关键是观察回答问题的难易程度。

如果真的很简单,你把它写下来,一切都很清楚,那么你的不确定性就很低。然而,如果你真的不知道如何回答,那么你就有很大的不确定性。

比知道你需要做什么更重要的是知道你不需要做什么。那些是非目标。

非目标是什么?如果你在一家软件公司工作,说“我不需要设计冲浪板”并不是一个非目标。那不是非目标,那只不过是无稽之谈。当然,你不想这么做。非目标实际上是非常合理的,甚至可能是你想做但你知道你不需要做的事情(比如“我需要支持低端设备”)。如果你能轻松地定义你的非目标,你就会知道你的不确定性还要低。

那么,为什么不确定性低很重要呢?当不确定性很高的时候,你将不得不做出权衡并保持灵活性。你可能会想“我可能需要支持这个特殊情况,所以我最好确保它是可能的”。你可能需要做出权衡来实现这种特殊情况。权衡的问题是,它们使事情变得复杂,不那么理想,因为你必须在相互冲突的关注点之间找到平衡。

当高度的不确定性导致你需要做出不必要的权衡时,事情就会变得让人不愉快。

不必要的权衡是万恶之源。

让我们来看一个例子:假设你正在为公司内部的移动应用程序构建基础设施,可能销售人员都会有内存较小的旧手机。因此,你设计的东西要可以在低内存设备上工作。但是,你公司会给每个人都买漂亮的新手机。所有支持低端设备的工作都成了不必要的,更糟糕的是,这可能会使软件在未来变得更加复杂。有趣的是,这是对Donald Knuth的“过早优化是万恶之源”的泛化。过早优化是不必要的折衷的一种特殊情况。每次你设计软件去做一些它并不真正需要支持的事情,就可能是一个不必要的取舍权衡。

我是在一个Web开发会议上做演讲,所以我希望有一个关于Web开发的部分。作为一个社区,我们已经构建Web框架好几年了。现在有20多年了。我们确实知道如何很好地构建Web框架。人们想用Web框架做什么的不确定性真的很低。

将此与下面这个场景进行下比较:假设现在是2015年,你的经理来了,说“我希望你为深度学习构建基础设施”。你可能会说,我从来没做过这种事。我们公司以前也没有人这样做过。关于这个话题,一共有三篇博客文章。不确定性真的很高。到2020年,你将无法构建出像Web框架一样优秀的基础设施。

处理不确定性

外科医生戴医用手套的照片,来自Getty

问题是,如果不确定性很高,我们该怎么做?我们要拟定抽象的程度。

让我解释一下:抽象非常棒。你希望尽可能多地抽象所有内容,因为抽象可以使所有内容真正具有表达性、更准确、可重用,而且通常更令人敬畏。然而,如果抽象不允许我们做我们想做的事情,那么事情很快就会落空。我们现在必须想办法解决它,打败它们然后做我们想做的事。一切都突然变得痛苦和可怕。

如果不确定性高,则降低抽象程度。

这个图表非常不科学地显示了不确定性和抽象性之间的关系。

这就是为什么我们需要很好地理解问题空间:它允许我们根据不确定性程度调整抽象。如果不确定性很高,那么我们就降低抽象程度。

抽象艺术的照片(图片来源)和女人的照片(图片来源,由Sheena876提供,遵循CC许可

解决所有已知的问题

每个软件项目都有一定程度的不确定性。因此,有一套有效的技术是很有用的——不管我们有多么不确定:我们将解决软件工程的所有已知问题。

迭代速度

第一种技术是优化迭代速度。我的意思是:你写了一些代码,然后在编辑器中点击保存,从那一刻到你发现修改是否存在问题需要多长时间?你可能有超级棒的热代码重载,所有东西都像魔术一样立即更新,或者你可能需要手动关闭Java应用程序服务器,重新编译,并重新启动服务器,这大约需要20分钟。我想我们很多人可能处于这两个极端之间。

实际上,缓慢的迭代周期就需要我们在软件设计时做出权衡。如果你花了很长时间才发现你犯了一个错误,那么作为一名软件设计师,最专业的做法就是说,“我必须设计这个API,这样才不会有人出错”。另一方面,如果一切都非常快,人们可以迭代地解决问题,因为失败的成本很低,那么作为API的设计者,你可以说,也许人们可以在这里做更多的探索。

可调试性

下一个是可调试性。作为框架作者,我们实际上对系统调试的难度有很大的影响。也许你设计了这个令人惊叹而复杂的黑盒状态机,没人能弄明白。也许你的堆栈跟踪超长且难于理解。或者,你可以设置一个合理的日志记录和跟踪系统,这样人们就可以理解正在发生的事情。他们犯了错误,他们调试它,他们修正它。这没有问题。

可测试性

与可调试性类似,你可以控制系统的可测试性。也许你的框架让实例化处于给定状态的东西变得非常困难,以至于无法进行测试?或者你让它变得很简单。人们将编写更多的测试,拥有更强的信心,并成为你的框架的更愉快的客户。

同理心

两只猫的照片,来自Getty。一个说“我不是什么都知道”。另一个说“没关系”。

对于解决软件工程中所有已知问题的最后一种技术,我们将稍微远离纯软件方面的东西。我们要谈的是同理心。

就像我在上次演讲中提到的:作为一个软件工程师,与其他软件工程师产生共鸣是一种简单模式下的共鸣。相比一个我们对其背景了解甚少的人,对于软件工程师,我们对他们对某件事情的感觉的直觉,更有可能是正确的。今天我想谈谈同理心的一个非常特殊的方面:你作为框架的设计者可以用它来构建完美的应用程序。你什么都知道。你可以把每件事都做好。但是其他使用你的框架的人对它的了解要少一些。

考虑一下,不完全了解你的框架意味着什么。

所以,考虑一下不完全了解你的框架意味着什么。以及如何在这些不完全了解它的用户面前使框架变得健壮。

变更部署

既然我们已经学会了解决所有已知的软件工程问题,那么让我们进入最后一章,变更部署。

如果没有用户,那么它就没有影响。

这部分非常非常关键。软件基础设施,不管它有多好,如果没有用户,那么它就没有影响。然而,在这个领域,人们在没有真正的用户的情况下建造象牙塔是非常普遍的。他们构建一些让他们兴奋的东西,构建过程可能真的很有趣。然后他们过来说,“嘿,我有个东西”,然后你说,“但是那个东西不能满足我的需要”,每个人都很伤心,然后他们继续建造下一个象牙塔。

如果你想专业化地构建软件基础设施,那么这显然不是正确的方法。让你的作品被采用就是一切。这是你工作的重要组成部分。因此,对于如何正确地做这件事,我的第一个建议实际上完全是关于市场营销领域。

软件工程师也是人。他们想要做一些他们认为很酷并且在Twitter上听说过的东西(比如无服务器、机器学习、虚拟DOM、今年大肆宣传的东西)。我认为,在你的框架中加入一些闪光点是很好的。只要它不是世界上最糟糕最不必要的设计权衡。让每个人都更快乐一点,不会造成什么伤害。这就是这次演讲的营销部分。让我们进入一个更严肃的话题。

增量采用

如果你可以增量地采用软件,则这个过程会更容易。这意味着,你不用这样,“嘿,试试这个新框架,我们用两年的时间重写了所有的东西,希望它不错”,而是一步一步地迁移较小的部分——因此,在用两年的时间完全重写之前,你就看到了积极的影响。我所看到的是,即使项目一开始的目标是完全重写,也会经常提前交付至少几个部分,因为双线工作(维护遗留系统和构建新系统)的压力会增加,而管理层又希望看到结果。

然而,对于框架设计者来说,增量采用之路并不好走。下面这些技术已经被证明在这种情况下是有效的。

遗留代码调制

第一种方法非常简单,分为两部分。

  1. 首先是将新的框架代码组合到遗留代码中:假设你还没有开始重写应用程序Shell。该应用程序仍然是建立在旧的架构上。但是团队过来说,“我要基于新框架构建新特性,并把它放到旧的代码库中。”也就是将新代码组合到遗留代码中。
  2. 另一方面是将遗留代码组合到新的代码库中。假设你有一个非常好的自定义组件,一个jQuery日期选择器,人们想要保留它,对吧?在你设计的框架中,这种jQuery的东西,可能违反了所有你曾经做过的假设,但在你的新代码库中仍然要可以使用。也就是将遗留代码组合到新代码中。

这两种策略都会给你的系统带来取舍压力,因为突然之间所有这些假设都不再有效了。但是这种权衡取舍可能是值得的,因为它允许人们增量地采用你的软件。

暂时的不完美

第二种技术我称之为暂时的不完美。作为一个框架作者,你可能对人们如何使用你的框架构建软件有一个非常理想化的看法。但是在增量采用的过程中,它可能会崩溃,因为并不是所有的东西都那么理想,而是新旧代码交织在一起。

我的建议是这样的:使用像ESLint这样的工具编写一个文本检查规则,它可以识别旧的编码方法。然后,当代码库中有代码没有以新的方式编写时,就会得到一个错误消息。最好错误消息还能告诉你一些有用的信息,比如“这种写法不再适用了。这里有一些文档可以告诉你如何正确编写。”

第二步是使用许可列表来允许所有已有的违规行为。这样一来,你就没有代码需要修复了,但是所有新代码都必须遵循新的编写方式。这非常有效,因为组织中可能有很多工程师,他们没有阅读电子邮件,不知道他们需要做一些不同的事情。现在他们看到了一条错误消息,就会转身去修复它。

技术债务分类账

账簿照片,来自Getty

还有一个更有趣的观点。你检入存储库的这个许可列表,实际上是一个技术债务分类账。技术债务可能就是这样一个抽象的概念,你知道你有,它可能在某个地方,但很难确定它在哪里。分类账可以让你知道文件X.js第15行有技术债务。

知道技术债务在哪里,是还清它的第一步。你可以让一个团队来解决它,你所要做的就是缩短许可列表。我认为这是一种让技术债务具体化的有力方式,就像你的银行账户债务一样。

自动迁移

下一项技术是支持自动迁移。它不会帮你做所有的事情,但它是相当强大的。像Facebook的codemod这样的工具可以帮助你实现这一过程。你声明如何从A到B,然后电脑完成剩下的工作。简直是太好了。

正如本文中的许多内容一样,针对自动迁移进行设计会给新API带来权衡取舍的压力,因为现在,在理想情况下,新API只需要现有代码库中已经可用的信息。但这可能是值得的,因为不需要人手工来做这些工作,这将使人们比手工做每件事更快乐。

零号客户

我们再来谈谈人。我希望每个人都能从这次演讲中学到的一点是,当你作为软件基础设施的负责人时,你总是需要一个零号客户。零号客户是象牙塔的解药。他们是你的第一个客户,你将帮助他们构建一个真正的产品。

零号客户是象牙塔的解药。

你需要做的是和他们一起编写软件——成为他们团队的一部分。这一切都发生在你宣布你的框架可交付之前,因为在这一步中,你可以实际验证你的假设。你可以找到一些极端的情况,你可以在迭代你的框架,直到它真的很好。第一个客户所做的不应该是迁移。它应该是从零开始构建一个东西。因为迁移总是有点痛苦、漫长和曲折,你真的不想迁移到还没有经过证明有效的东西上。

作为基础设施工程师,一个比较讽刺的地方是,你很少使用自己的基础设施来构建任何东西。这使得编写基础设施入门文档变得非常困难。如果你有一个零号客户,你和他们一起工作,这是最好的机会,你可以为他们编写入门文档。不要错过机会!

实际上,在大型组织中,找到零号客户是很有挑战性的。你可能找不到一个团队愿意使用不稳定且可能相当糟糕的框架。我发现了一些很好的论据来说服一个团队成为你的零号客户:

  • 通常会有喜欢走在前沿的团队。你可以做的一件事是说,“嘿,你最终将不得不迁移到这个,因为它是未来每个人都必须使用的技术栈。你为什么不想现在就开始做一件新的事情,跳过迁移呢?”这对一个团队来说是很有吸引力的。你可以争辩说,因为你要和团队一起工作,他们可以获得一个为他们量身定做的框架,而不用像其他人那样要凑合着用现有的框架。
  • 如果这招不奏效,你就得采取更严厉的策略。在这种情况下,组织必须足够成熟,同意强制执行。有人将不得不决定哪个团队将成为零号客户,他们将不得不接受它。因为另一种选择是建立一个象牙塔。这对所有人都不好,不仅仅是这一个团队。

零号迁移

最后,我们会第一次将现有的代码库迁移到我们的新的基础设施。再一次,你要自己进行迁移。这是因为你的框架实际上可能还不是一个很好的迁移目标。假设你的框架在B点,某个项目在A点。从A点到B点可能超级困难。但是,作为框架的作者,你可能会看到对B点进行小幅修改的机会,从而更容易实现目标。因为你是一个懒惰的人,你将使迁移变得非常容易,这将帮助每一个在你之后迁移的人,让他们有一段更美好的时光。

小结

这是我想谈的三件事。让我快速地总结一下:

  1. 我们需要做的第一件事是了解不确定性的程度,然后分别调整抽象的程度。
  2. 即使不确定性很高,我们也希望解决所有已知的问题:增加迭代速度,使软件可调试、可测试,对于人们可能不知道的事情,我们要有同理心。
  3. 最后,如果你不关注团队采用,那么这些就都无关紧要了。让你的软件变得招人喜欢,专注于增量采用,做一个技术债务的分类账,找到零号客户,完成零号迁移。

今天就谈这些。非常感谢!

原文链接:

Designing Even Larger Applications

本文最初发布于Medium博客,经原作者授权由InfoQ中文站翻译并分享。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/AHpXpbGy7RMGc1F4Ow7W
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券