一、自我介绍

我是不四,毕业后一直在阿里和蚂蚁工作,不四是我在阿里的花名,社区中一般以另一个花名 “死马” 出现。工作这 8 年多来一直专注在 Node.js 和 Web 开发领域,也在社区参与了一些开源项目,包括 KoaEggcnpm 等,非常幸运在 Node 出生之初就开始参与其中,算是赶上了一波由 Node 带来的大前端变革浪潮。每一个人的成长轨迹都不一样,一路上遇到的机遇也各不相同,这次分享也仅站在一个普通工程师的角度来分享我的成长经历和贯穿其中的一些个人习惯。

二、成长历程

image.png

实习

在 2011 年的夏天,大三暑假我来到了当时的淘宝数据平台实习。也不知道是运气好还是运气差,我是以 C++ 工程师的身份被招聘的,分配到的数据产品部却是一个做 Web 产品的团队,还是用刚刚出生的 Node.js 作为服务端开发语言,并在实践全栈研发,还记得那时候 Node 的版本才 0.4,而我是一个连 JS 和 JSP 都分不清楚的菜鸟,大学三年只写过黑框框的 C++,连 HTTP 是什么都不知道,无比忐忑的开始闷头学习 JS 基础。

image.png
多年以后和当时看的入门教材作者成为了同事

幸运的是,当时的团队大牛云集,国内第一批 Node.js 的布道者,Node Party 的发起人空无、清笃、玄澄,以及国内 Node 社区一直以来的核心贡献者苏千和朴灵等等都集中在了这个团队。跟随者他们的脚步,我在大半年的实习时间内,顺利的将 C++ 给忘光了,成为了一名新手 JS 工程师。

数据产品部

12 年毕业后正式入职淘宝数据产品部,那是大数据最火热的年代,我们坐在淘宝数据平台的金山上,挖掘出来了数据魔方、淘宝指数、淘宝时光机等数据产品。随着我慢慢的深入业务,也逐渐理解了团队为什么选择 Node 技术栈。大部分的数据产品本身的计算和业务逻辑相对不会太复杂,依赖大量后端数据源提供数据,是一个典型的 IO 密集型应用。而 JS 全栈也可以带来更高效的研发效率。

image.png
数据魔方

image.png
淘宝时光机

随着数据产品覆盖的场景越来越多,我们需要对接到阿里集团的各种内部系统。所以我们用 Node 实现了内部的微服务框架、登录系统、配置系统等中间件。而随着 Node 生态的越来越繁荣,搭建一个内部的 NPM 包管理系统也提上了日程。我们尝试着用 NPM 官方的解决方案搭建,但是难以运维,也不能完全满足需求,最后我们开发了 cnpm 用来搭建内部的 NPM 包管理平台并提供了国内的 NPM 镜像。后来的事实证明,一个快速的 NPM 包管理平台对于促进 Node 和大前端社区的繁荣起到了至关重要的作用。

刚毕业的这两年是我技术成长非常快的时候,一方面是团队有很多大牛可以学习,另一方面也赶上了一波 Node 技术初生的福利期,我也在这里完成了工作后的第一次晋升,从 P4 晋升到 P5。

天猫前端

在 14 年中的时候,由于团队的一些变化,我转岗到了天猫前端团队。当时的天猫前端团队其实没有专职的 Node 开发工程师,团队遇到的一个很大挑战是运营活动页面之前都是运行在 PHP 上,随着 PHP 工程师在阿里的逐渐减少,那套年久失修的 PHP 系统已经难以继续支撑流量越来越夸张的双十一活动了。所以我开始着手通过 Node 实现新一代的页面渲染服务。

新服务在 14 年双十一的时候在天猫首页进行验证,从性能稳定性上比老的 PHP 服务高出很多。接下来我们开始基于新的服务上层实现了新的可视化页面搭建系统,非常完美的支持了 15 年的双十一,这套系统也一直服务到现在,当然已经进化的更加完备和复杂了。

image.png
当时给 php 和 Node.js 系统做的 benchmark

在天猫可以说是之前那段工作经历积累后的爆发期,用一个全新的技术栈实现了一个重要的业务系统,并取得了很大的业务价值,所以在 15 年的时候我也从 P5 晋升到了 P7。

蚂蚁体验技术部

可能还是有想做更底层一点技术的念头,我在 16 年初的时候决定从天猫前端团队转岗到蚂蚁的体验技术部给大前端团队做内部的 Web 框架和 BFF 研发模式的支持。其实在去蚂蚁之前,我也一直在维护者 Koa 和一些 Web 框架生态和中间件的服务,到蚂蚁之后参与做的第一件事情就是从当时蚂蚁的 Web 框架 Chair 中抽出来了 Egg.js,以统一阿里经济体各不同 BU 的大前端 Web 研发体系。Egg.js 也随后面向社区开源。

image.png
Egg.js 生态

通过两年多时间的发展,BFF 研发模式也慢慢的被蚂蚁、阿里经济体甚至是国内接受了。我也在这里晋升到了 P8。

语雀

随着一次内部组织架构调整的机会,我来到了语雀团队。它是蚂蚁体验技术部内部的一个创新孵化项目,为十万阿里人提供知识协同和文档管理的服务,18 年的时候,语雀也开始对外服务。兜兜转转的走了一圈,我又回到了使用 JS 全栈进行应用开发。在语雀团队,我们践行着产品工程师文化,高效的完成产品研发。

image.png
语雀产品工程师文化

简短的总结毕业后的这 8 年,我在一个公司内兜兜转转,但是一直专注在一个技术领域上,并在底层技术、基础服务、产品研发等不同的方向做探索。成长过程中可能有很多的幸运,你能遇到什么样的团队和老板,可以做什么样的事情,这些可能我们都很难完全控制,但是我们能够控制的是作为一个工程师,你如何提升自己的技术能力,做好抓住机会的准备。

三、工程师成长密码

回过头再来看这几年,我在工作和社区中养成了一些习惯,这些习惯可能是对我的技术成长影响非常大的。

坚持写代码

显而易见,作为一个工程师,我们最重要的职责就是写代码。熟能生巧,坚持写代码一定是工程师成长手册中最重要的一点。不论是在做技术项目还是带团队,写代码一定是我日常工作中最重要的一部分。

image.png

然而低质量的重复是毫无意义的,我们要坚持写代码,更要坚持写好代码

什么是好的代码?这个问题可能不同的人眼中有不同的答案,对我而言,好的代码起码要满足这三个条件:

  1. 好的代码是简单的,简单的代码架构清晰,并且让编码变的更轻松;
  2. 好的代码是给人看的,绝大部分的应用都是要持续维护的,不给别人挖坑,也是不给未来的自己挖坑;
  3. 好的代码是可测试的,通过编写单元测试,既保障代码的逻辑完备,减少 Bug,也利于后期维护与重构。

保持代码简洁

先从简单说起,简单的代码是容易理解的,然而想要编写简单的代码,架构起来又是更复杂的。这是给自己提出更高的要求,不断优化重构,在这个过程中得到成长。当遇到一些复杂需求的时候,我始终坚信一点:如果一个逻辑我们作为实现者都很难梳理清楚,代码中一堆的 if else 条件判断,那用户也一定是无法理解的。

所以当遇到这种情况,我们需要从产品侧和架构侧去思考,到底是什么原因导致了复杂度?我们应该是去优化产品需求还是去优化底层架构。这也会迫使我们在产品和架构上有更深入的思考。

Code Review

另外一个我和团队一直在坚持的习惯是 Code Review。CR 是一个非常好的提升代码质量的方式,它是需要团队投入大量精力的事情,但是一定收获不菲。

image.png

我们直面 CR 的灵魂三问:

  1. 为什么要做 CR?业务催的那么紧,哪有时间做 CR?
  2. 谁来做 CR?是团队的主管、核心工程师才能帮别人 CR 吗?
  3. CR 是一件很费精力的事情,如何才能坚持下来呢

image.png
Code Review 的主体是人, Review 的对象是代码

  • 对于代码提交者的人来说,当你知道你的代码会被其他人看到的时候,是肯定会更加注重代码质量的。我还记得我开始向社区成熟开源项目提交代码的时候感受到的巨大压力,它会让你在提交代码前更仔细的设计和编码。经过一次次的 CR 后,你会发现你的代码质量会飞速提升。
  • 对于评审人来说,每一次 CR 都可以增加你对整体代码库或者不同业务的熟悉程度,还可以传授经验、提升团队影响力。
  • 最后,通过 Code Review,我们可以提升项目代码的质量与可维护性,统一团队的代码风格,并让每一个业务逻辑都能尽量找到 back up。CR 是一件三赢的事情

CR 是一件很有意义的事情,但是我们应该怎么去做呢?

第一步还是要从自己做起,通过自己的主动与坚持,带动团队一起参与。在提交代码的时候,写好 commit message,做好自测,注意代码的可读性。抽时间来 Review 团队其他人的代码,为每一行代码负责。

image.png

但是 CR 毕竟还是一个团队的事情,如何保持团队 CR 的质量呢?我们一定要严抓新人的第一次 CR。一般来说我们团队新人的第一个 PR 都会收到比较大的挑战。新人对代码不熟悉,编码风格也和团队可能不一致,第一次 CR 非常重要,需要让大家都对齐对代码质量的标准和要求。

image.png
对新人重拳出击

除了代码之外,更上层的设计与架构也需要做 Review,让每一次系统功能设计、架构升级都经过 Review,不仅可以让系统更稳定,也可以快速提升自己的系统架构能力。

Unit Tests
  • 你的代码质量如何度量?
  • 你是如何保证代码质量?
  • 你敢随时重构代码吗?
  • 你是如何确保重构的代码依然保持正确性?
  • 你是否有足够信心在没有测试的情况下随时发布你的代码?

如果对这些问题没有答案,或者没有 100% 的信心,那你需要给你的代码做单元测试。

现在说起单元测试,大家其实还是有体感的。然而在 12、13 年我刚工作的时候,单元测试还是一个相对陌生的概念,但是当时的 node 开源社区其实已经慢慢开始流行起来写单元测试了,当你给其他开源项目提交代码的时候,没有测试是不可能被合并的。当时高产的 TJ 不仅仅提供了 Express connect 等 Web 框架,同时也提供了一系列底层配套的测试模块,包括测试用例驱动器 Mocha,断言库 should.js,http 请求测试库 supertest 等等。

跟随社区一起,我们很早就把单元测试引入了我们的工作中,基本上从我正式工作开始编写的第一行项目代码开始就在写单元测试了,这个习惯让我在 8 年的工作经历中保持了不错的代码质量,在没有测试工程师测试过我的代码的情况下,没有搞出重大故障被阿里开除。

说起对业务代码写单元测试,可能大家的第一反应还是哪有时间写单元测试啊?其实写单测真的没有那么耗时,只要你找对工具和方法。对于单元测试来说,只需要四个步骤:

  1. 创建一些初始数据;
  2. 对外部依赖进行 mock;
  3. 最小粒度的执行要测试的方法;
  4. 对结果做断言。

image.png

剩下的就是按照这个方式构造测试用例输入,尽可能的覆盖代码中的每一个分支和边缘场景。

Web 应用中的单元测试更加重要,在 Web 产品快速迭代的时期,每个测试用例都给应用的稳定性提供了一层保障。API 升级,测试用例可以很好地检查代码是否向下兼容。对于各种可能的输入,一旦测试覆盖,都能明确它的输出。 代码改动后,可以通过测试结果判断代码的改动是否影响已确定的结果。我们在做 Egg.js 的时候,最重要的一件事情就是给它编写对应的测试框架,让业务方能够更简单、没负担的写单测来保障代码质量。

而把代码的覆盖率提高,看到测试覆盖率的报告全绿,不仅对上线代码更有信心了,同时也是一件很有成就感的事情。

image.png

持续分享

如果说坚持写代码是练习和输入,那另一个对我成长帮助很大的习惯就是分享,这看起来是一个对外的输出,但在我看来它更是一个非常好的学习机会。

在我刚工作在数据产品部的时候,我们团队组织了一个 Show Me The Code 的内部分享沙龙,是一个形式非常随意的分享会,不需要准备正式的 PPT,不需要很长的分享时长,就简单的分享一下最近学到的新知识,看到的有趣的代码。这个培养了我去分享的习惯。那时候经常为了找一个分享的话题,去主动研究一些新的模块,看他的源码,自己也会去造一些小的轮子来解决实际工作中遇到的重复性工作。而现在在语雀团队,我也在组织内部双周分享会,已经坚持了一年多的时间了。

image.png
再小的分享也会有收获

工作的这些年我也陆陆续续在外面的会议进行了不少的外部分享。基本上每一次分享,都需要自己先认真的对要分享的内容查漏补缺,并尝试着将它准备到浅显易懂。每一次演讲过后,都会让你对这个演讲主题的领域有更深的感受。

image.png

分享对我来说更多的是给我提供了一个非常好的阶段性总结的机会,最好的学习方法就是教会别人,因为谁也不想在台上出糗对吧。去听一场技术分享,听到的知识转眼就忘了,而你认真的去准备一场演讲,那场演讲收获最大的一定是你自己。

参与开源

对我的技术成长影响很大的另一个因素是开源,当然开源本质上也是一种分享。

现在是一个百花齐放的年代,开源世界的项目越来越多,根据 GitHub 的数据,2019 年有 4400 万个新项目被创建。每一个有技术追求的工程师可能都想过要去 GitHub 上写点什么。但是开源并不是指的在 GitHub 上提交代码,开源更多的是一种心态。

  1. 开源意味着你要将你的代码给所有的开发者审阅,就像前面 Code Review 的时候说的,把代码给别人看是一件很有压力的事情,更何况提交到 GitHub 后,所有人都能看到,你的同事、面试官都能看到。一定要认真的对待每一行代码,每一次提交。
  2. 同时当有人使用你的开源项目时,意味着你要承担起责任。尽管开源协议可能是很宽松的 MIT,但还是要对你写的代码负责。
  3. 开源应该让你感受到 “痛苦”,需要对开源代码提出更严格的要求,追求最优的代码架构,测试完备,描述清晰,编写高质量代码的过程会让你感受到痛苦,但是会有更快的成长。

可能我们有时候很难自己想到一个很好的想法,或者很难自己实现一个那么高质量的开源项目,不要着急,参与开源的门槛其实也没那么高。

第一步,挑选一个工作中可以用到的领域的高质量开源项目,为什么工作中可以用到很重要,因为这样你才能更好的找到改进的方向,找到痛点。例如我当时选择深入参与的开源项目是 Koa,因为我工作的重点也是在 Web 研发领域,而我觉得 Koa 当时基于 co 提供的那套异步编程模型一定是未来的趋势。

第二步,逐步参与进去,一开始可能只是修一下文档,找找 Bug 修复一下,补充几个测试用例。慢慢的随着你对代码和周边生态的完善,可以进一步去实现一些缺失的周边生态,尝试根据自己实际遇到的问题给项目提交一些功能改善。

国外有很多高质量的项目,我们也可以帮他们做文档的翻译。不要小瞧文档翻译,翻译一遍文档就意味着你要深入理解这个项目,其实也是一件很难并且很有收获的事情,还能够提升社区影响力。

开源是一个成就感驱动的事情,因为你无法从中获得看得着的收益。所以能够持续的参与开源最重要的一点是你要能够从中找到成就感。

image.png

四、和团队一起成长

说了这么多个人成长的事情,我还是想再稍微说一下团队。我们作为个人在团队中工作,只有帮助团队一起成长,拿到业务价值,才能将我们的技术成长 “变现”。而之前提到的 Code Review 亦或是单元测试,都是需要团队一起来配合的。

  1. 如果你的团队还没有内部的分享会,尝试着自己去组织一个定期的内部分享会;
  2. 从现在开始做 CR 和单测,用自己来影响团队;
  3. 尝试着在团队中建立契合团队的工程师文化。

前两点其实在之前的分享中都已经提及到了,这也是我们团队一直在坚持和倡导的。我想说一下语雀的团队文化,在语雀我们希望每一个工程师都是产品工程师,他是产品的技术合伙人,参与产品讨论、完成产品功能研发,同时他也是某一个具体领域的技术专家,例如前端 UI 组件、编辑器领域、服务端领域等等。在我们的工作流程中,所有的工程师都是以全栈的身份参与项目研发,跟进项目从产品设计、到系统分析、研发自测,CR 以及上线的全流程。通过产品工程师文化,我们鼓励每一个工程师都能够在业务和技术上找到自己的成长方向,并陪着团队和业务一起快速成长。

个人成长很重要,同时想要取得好的结果得到晋升,一定要将个人的成长和团队的成长绑定起来。通过把自己的事情做到极致,帮助团队创造业务价值,在这个过程中提升自己在团队内外的影响力。找到那个个人和团队双赢的点去发力,可以得到事半功倍的效果。