Files
CategoryResourceRepost/极客时间专栏/研发效率破局之道/个人效能/21 | 高效工作:Facebook的10x程序员效率心法.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

126 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="21 | 高效工作Facebook的10x程序员效率心法" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ae/69/ae007b6781bc727c2e4e6d26b85bac69.mp3"></audio>
你好,我是葛俊。从今天这篇文章开始,我们就正式进入个人效能模块了。今天,我要和你分享的主题是,程序员如何高效地进行开发工作。
最近比较流行的一个说法是10x程序员也就是10倍程序员意思是一个好的程序员工作效率可以达到普通程序员的10倍。要做到这一点并不容易我们需要在编程技术、工作方式、工具使用等方面全面提高。
今天这篇文章我将聚焦于如何提高自己的编程技术给出在实践中被证明有效的3条原则包括抽象和分而治之、快速迭代以及DRYDont Repeat Yourself并针对每条原则给出几个高效实践。而关于工作方式、工具使用等方面的内容我会在后面几篇文章中与你详细讨论。
## 第一条原则:抽象和分而治之
虽然我们面对的世界非常复杂,但大脑只能同时处理有限的信息,那怎么平衡这个有限和复杂之间的矛盾呢?
**最好的办法是**,把一个系统拆分为几个有限的子系统,每个子系统涵盖某一方面的内容,并将其复杂性隐藏起来,只对外暴露关键信息。
这样,我们在研究这个系统的时候,就无需考虑其子系统的细节,从而对整个系统进行有效的思考。如果我们需要深入了解某一个子系统,再打开这个子系统来看即可。
以此类推,如果这个子系统还是很复杂,我们可以再对其进行拆分。这样一来,在任何时候,我们思考时面对的元素都是有限的,复杂度也下降到了大脑的能力范围之内,从而完成对一个复杂系统的理解和处理。
**这个拆分处理的过程,就是我们常说的分而治之;而用子系统来隐藏一个领域的内部细节,就是抽象。**抽象和分而治之,是我们理解世界的基础。
比如我们在了解一张简单的桌子时首先想到的是它由1个桌面和4条桌腿组成。那么桌面和桌腿就是子系统桌面就是一个抽象代表实现摆放物品功能的一个平面桌腿也是一个抽象代表支撑桌面的结构。
如果我们需要进一步了解桌面或者桌腿这两个子系统,可以再进一步去看它们的细节,比如两者都有形状、重量、材料、颜色等。但如果一上来就考虑这些细节的话,我们对桌子的理解就会陷入无尽的细节当中,无法快速形成对整个桌子的认知。
软件开发也是这个道理,我们必须做好抽象和分而治之,才能做出好的程序。
所以,拿到一个任务之后,我们要做的**首先就是进行模块的定义,也就是抽象,然后对其分而治之**。
为方便理解我再和你分享一个在Facebook时几个前后端开发者同时开发一个功能的案例吧。
这个功能由一个前端开发者和两个后端开发者完成整个研发过程至少涉及3个抽象和分而治之的操作
- 第一步前后端模块进行自然的拆分。这时前后端开发者一定会一块儿认真讨论明确前后端代码运行时的流程后端需要提供的API以及交付这些API的时间。
- 第二步,两个后端开发者对后端工作进行拆分,确定各自的工作任务和边界。
- 第三步,每个开发者对自己负责的部分再进行抽象和拆分。
在这个过程中一定要明确模块之间的依赖关系尽快确定接口规格和可调用性。比如在前后端的拆分中常常会采用这几个步骤处理API
1. 前后端开发者一起讨论明确需要的API。
1. 后端人员会先实现API的Mock返回符合格式规范的数据。在这个过程中后端开发者会尽快发出代码审查的要求给另一个后端和前端开发者以确保格式正确。
1. Mock实现之后尽快推到主仓的master上(也就是origin/master),并尽快将其部署到内部测试环境,让前端开发者可以使用内部测试环境进行开发和调试。
1. 这些API还不能面对用户通常会先使用功能开关让它只对公司开发人员可见。这样的话即使API的代码在origin/master上部署到了生产环境也不会对用户产生影响。
通过这样的操作,前后端的任务拆分就顺利完成了。
提高抽象和分而治之效率的一个技巧是,在设计代码架构时注意**寻找合适的设计模式**。
设计模式指的是,设计过程中可以反复使用的、可以解决特定问题的设计方法,最经典的莫过于[《设计模式:可复用面向对象软件的基础》](https://book.douban.com/subject/1052241/)中列举的23个设计模式以及针对企业软件架构的[《企业应用架构模式》](https://book.douban.com/subject/1230559/)。同时,我们还要注意公司内部具体的常用模式。这些模式都是经实践检验有效的,且传播较广容易理解,都可以作为你进行模块拆分的参照。
具体实现功能的过程中,也会处处体现分而治之的思想。最主要的一个表现是,每个开发者都会把自己的**代码尽量做到原子性**。代码的原子性指的是,一个提交包含一个不可分割的特性、修复或者优化。
在实际工作中,功能往往比较大。如果只用一个提交完成一个功能,那这个提交往往会比较大,所以我们需要把这个功能再拆分为子功能。
比如某个后端API的实现我们很可能会把它拆分成数据模型和API业务两部分但如果这样的提交还是太大的话可以进一步将其拆小把API业务再分为重构和添加新业务两部分。
总之我们的目的是让每个提交都做成能够独立完成一些任务但是又不太大。一般来说一个提交通常不超过800行代码。
## 第二条原则:快速迭代
通过前面的文章,我们已经明确了快速迭代对提高研发效能的重要意义。接下来,我们就看看在具体的编程中,快速迭代的一些实践吧。
**第一,不要追求完美,不要过度计划,而是要尽快实现功能,通过不断迭代来完善。**优秀的架构往往不是设计出来的,而是在实现过程中逐步发展、完善起来的。
Facebook有一条常见的海报标语叫作“Done is better than perfect”意思就是完成比完美要重要。要实现快速迭代我们在设计和实现功能时都要注意简单化。
有些开发者过于追求技术,投入了大量时间去设计精美、复杂的系统。这样做没有问题,但一定要有一个度,切忌杀鸡用牛刀。因为复杂的系统虽然精美,但往往不容易理解,维护成本也比较高,修改起来更是不容易。
所以我们在Facebook进行开发的时候尽量使用简单实用的设计然后快速进行版本迭代。
**第二,在设计的实现中,尽量让自己的代码能够尽快运行起来,从而尽快地验证结果。**我们常常会先实现一个可以运行起来的脚手架,然后再持续地往里面添加内容。
在工作中,因为往往是在一个比较大的系统里工作,不能很容易地运行新代码。这时,我们可以编写脚本或者单元测试用例来触发新写的代码。通常情况下,我们更倾向于使用后者,因为这些测试用例,在功能开发完成上线之后,还可以继续用于保证代码质量。
在我看来,在开发过程中,能触发新写的代码帮助我开发,是单元测试的一个重要功能。
**第三,为了能够快速进行验证,一个重要实践是设置好本地的代码检验**包括静态扫描、相关单元测试的方便运行以及IDE能够进行的实时检查等。
**第四,代码写好之后,尽快提交到主代码仓并保证不会阻塞其他开发人员**
实际上这是代码提交原子性的另外一个重要特点即代码提交的原子性可以保证主代码仓在理论上能够随时基于master分支上的任何提交构建出可以运行的、直接面对用户的产品。在这种方式下每个开发者在任何时候都可以基于origin/master进行开发从而确保Facebook几千人共主干开发时分而治之能够顺利进行。
关于实现代码提交的原子性我还有一个小技巧就是如果当前编写的代码提交实在不方便马上推送到origin/master分支上我们也可以频繁地fetch origin/master的代码到本地并在本地对orgin/master进行rebase来解决冲突。这样就可以确保我们开发的代码是基于最新的主仓代码从而降低代码完成之后push时冲突的可能性。
## 第三条原则DRY
DRY也就是不要重复你自己是很多开发模式的基础也是我们非常熟悉的一条开发原则了。比如我们把一段经常使用的代码封装到一个函数里在使用它的地方直接调用这个函数就是一个最基本的DRY。
**代码逻辑的重复**,不仅仅是工作量的浪费,还会大大降低代码的质量和可维护性。所以,我们在开发时,需要留意重复的代码逻辑,并进行适当的处理。
具体来说首先是寻找重复的逻辑和代码。在动手实现功能之前我们会花一些时间在内部代码仓和知识库中进行查找寻找是否有类似的功能实现以及一些底层可以复用的库过程中也可以直接联系类似功能的实现者进行讨论和寻求帮助。另外有一些IDE比如Intellij IDEA可以在编码的过程中自动探测项目中可能的代码重复。
找到重复的逻辑和代码之后,主要的处理方式是,把共同的部分抽象出来,封装到一个模块、类或者函数等结构中去。
如果在开发新功能时发现有需要重构的地方,一个常见的有效办法是,先用几个提交完成重构,然后再基于重构用几个提交实现新功能。
在编程工作中,除了代码的重复外,比较常见的还有**流程的重复**。比如测试中,我们常常需要重复地产生一些测试数据,运行完测试之后再把这些数据删除。
这些重复的流程也需要DRY最主要的办法是自动化。以重复的测试数据产生、删除流程为例一般的做法是编写脚本进行自动化当然有些时候也需要写一些复杂的可执行程序来生成数据。
流程重复还有一个特点是,它常常和团队相关,也就是说很多成员可能都会重复某些操作,这样的操作更值得自动化。比如,团队的很多成员常常都需要产生测试数据,这时我推荐你主动对其进行自动化、通用化,并提交到代码仓的工具文件夹中供团队使用。
## 小结
今天我针对如何使自己成长为10x程序员首先给出了在编程技术方面的3个原则分别是抽象和分而治之、快速迭代以及DRY。然后针对每一条原则我给出了Facebook高效开发者的一些常用实践。
其实我们还可以从这3条原则中延伸出其他很多有效的实践。
比如,好的代码注释。对子系统设计进行合理的注解,可以方便其他开发者在不同的抽象层面对软件结构有更直观的了解。而且如果系统拆分得当的话,需要注释的地方就会比较少。又比如,代码的设计时审查,就是帮助我们及早进行架构讨论,从而实现快速迭代。
为方便你理解并运用到自己的开发工作中,我将这些实践总结到了一张表格中,如下所示。
<img src="https://static001.geekbang.org/resource/image/cc/d1/ccb6ec868814aa9d86a3558739cd8dd1.jpg" alt="">
另外关于编程技术的高效实践也是不断演化和发展的。以设计模式为例最近几年又出现了针对Kubernetes开发场景的模式你可以参考[《Kubernetes Patterns》](https://www.amazon.com/Kubernetes-Patterns-Designing-Cloud-Native-Applications-ebook/dp/B07QH3JCC6/ref=sr_1_1?crid=1M8SGJX67RR5J&amp;keywords=kubernetes+patterns&amp;qid=1570073661&amp;sprefix=kubernetes%2Caps%2C335&amp;sr=8-1)这本书针对云原生Cloud Native开发也有了业界比较认可的[12-factor原则](https://12factor.net/zh_cn/)等。将来必定还会有其他新的设计模式产生。比如伴随着AI的逐渐成熟针对AI的设计模式必定会出现。
所以,作为一名软件开发者,我们必须要持续学习。我之前在一家创业公司时,有一个刚大学毕业两年的同事,他有一个非常好的习惯,就是每天早上比其他同事早半个小时到办公室,专门来学习和提高自己。正是因为他的持续学习,使得他虽然工作时间不长,但在整个团队里一直处于技术领先的位置。你也可以借鉴这个方法,或者采用其他适合自己的方法来持续地提升自己。
## 思考题
1. 我今天提到的关于分而治之的实践,哪一条对你触动最大呢?同时,也和我分享一下你在工作实践中的感受吧。
1. 你还知道哪些编程技术方面的高效原则和实践吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!