前言
很久没打开github的博客(内心os: wocao! 以前还写过这玩意)
更新时间停留在两年前。趁着最近有空,记录一下这个阶段工作上的一些心路历程。
机缘巧合
大概是两年前,那时候收到了几家公司的offer,正准备给答复时候,目前就职的公司(富融科技)HR联系上我,希望进行面试再对比一下。当时其实我不想面也表示了婉拒,但是HR说可以当天把我的面试流程线上走完。于是,经历了上午的导师一面,下午的组长二面,晚上的中心总监复面;第二天告知我顺利通过了面试。
在面试结束后,我从最初的无所谓心态到有所期待,主要得益于面试过程中与面试官的交流过程相当顺畅(确认过眼神,是我想一起共事的同事)。
我入职时候title是前端开发工程师,围绕银行背景下SDLC开展工作,包括不限于制品管理,交付确定、指令执行等方面。但事实上,除了前端,什么都干(开玩笑的,这并不是什么坏事)。在日常工作中,干的活与腾讯同事他们并没有多大差异,团队人员分布情况是:9个腾讯同事、3个子公司同事以及5个外包同事。
就是这么机缘巧合,进入了富融科技。公司属于腾讯旗下的子公司,说实话,整体福利待遇不算差,除了工资与腾讯员工有比较大大大大的差异。
抓住机会
在腾讯系的工作环境下要求自己视野不仅只局限于前端,包括后端、运维、产品定位等;同时要求自身需要有足够沟通,表达能力,在短时间能把自己的想法说清楚,别人能够有效接收到你的消息。在团队中,主要有三方面的工作成果:
a.现有功能的优化;
b.新业务的架构与落地;
c.团队基础公共的能力建设。
接下来复盘下一些印象的工作内容。
CD采集逻辑的优化
我们组其中一个核心业务功能是需要把流水线平台的制品采集到自身的devops平台进行交付。
流水线 => 定时器采集 => devops平台
在原本的实现中,是通过egg的schedule通过定时器,走接口进行拉取同步。这看起来挺简单,然而实际上,需要考虑到:
1.一个研发单是否对应多个版本需求
2.一个研发单是否存在多条流水线
3.一条流水线存在多个环境阶段
4.如何标准化制品入库
以上导致这部分代码逻辑成为项目中复杂度最高,最重的一部分,然而这部分业务都交给了我去维护优化。
现状:
- 通过定时器,以devops平台全量版本维度进行同步,粒度过大;
- 数据量起来后,p-limit请求限制拉低同步效率;
- 同步过程对开发来说,相当于黑盒子,出问题时候难以定位
目标:
- 同步粒度控制到流水线实例,即模块;
- 主动触发同步,提高效率,消息通知等;
- 引入rabbitmq消息队列,保证同步可靠性
最终达到什么效果呢?
原本同步效率平均需要10分钟左右,目前可以控制到1分钟内。(给自己点个赞。
工程化建设
在团队中,另一方面的工作重点是开发维护项目的基础建设。从monorepo的创建,制定目录规范,npm打包规则等,公共基础功能的抽离等,是实话是个爬坑的过程。
依赖包管理问题
在这个部分,遇到最大的难题就是monorepo下依赖管理混乱的情况,这也是monorepo的痛点之一。
最初我们是通过learn workspace,使用yarn进行依赖包管理,众所周知,yarn在安装依赖时候,会将依赖包上升到底层(项目根目录),在nodo的require机制下,依赖包的管理相当混乱。举个例子:
project-a的package.json没有安装lodash;
project-b的package.json安装了lodash@1.0.0;
project-c的package.json安装了lodash@2.0.0;
在yarn下,会存有两个问题:
1.package-a在不安装lodash的情况下,能在代码中引入lodash,开发环境正常运行;
2.project-b require时候有可能引入到了lodash@2.0.0版本。
这就是业界常说的幽灵依赖和依赖分身。
同时在ci环境,无法读取到private libs包,只能在流水线通过脚本强行把libs打包后的文件复制到对应项目的node_modules下。
因此我们考虑切入pnpm,pnpm的原理及优势这里不展开说了,网上有一推相关的资料说明。我总结下就是,在pnpm模式下,每个project都有独立的node_modules,各自项目的依赖都在自身的node_modules下找,不在层层上升,保证依赖包之间的纯净。
依赖注入问题
我们使用了nestjs框架作为后端服务,nestjs的其中一个特点是模块的依赖注入,通过容器管理降低实例的创建成本。
在引入pnpm后,我们遇到了一个很大的问题,@nestjs/core包在容器ioc寻找依赖时候,无法找到唯一实例,导致启动时候无法连接db module。
这个也是pnpm官方的一个issue,最终还是团队的大神,通过硬链方式保证了@nestjs/core只会被创建一次,保证唯一。respect!
回想项目最初从lerna workspace雏形,到现在nx pnpm下多项目多包稳定运行,成就感还是挺强的。
或者你会有疑问,为什么需要monorepo?
- 业务涉及多平台,享有相同的工作流程(比如ci, build,eslint等);
- 多个项目相互依赖,需要管理版本升级
- 复用沉淀公共的基础逻辑,如鉴权、统一错误处理等
如果有涉及上面几点,这时候就需要考虑使用monorepo方案了;
在monorepo下,代码的实现越下层,意味着对你的设计,以及技术能力要求更高。这里更多考虑多平台的通用性。在超过十人的团队中,如果你可以无侵入舒服使用了同事封装好的组件模块,那这位同事一定是在实现花了心思去设计的。
至此,monorepo已经落地稳定有一段时间,至此已经是完美了吗?
并不是,在实际使用中,使用nest-cli创建nestjs新项目时候,往往需要再在package.json二次修改nestjs相关的依赖库,达到版本统一(原因在上面依赖注入中有提及)。对新进来的同学并不太友好,存在较大的沟通成本。
于是,考虑使用脚手架规范项目目录结构以及版本,快速了解下,撸了一个,仅供参考哈:github地址。
系统性思考能力的提升
在团队中,感觉自己也是比较幸运的,能接触到比较关键的业务实现,也感谢leader给予的机会。作为一个开发者,其实80%的工作都是可代替性的,即你能做的东西其他人也能做;关键是剩下的20%的东西你能不能做,能做到什么水平,或许这就是你在考核中的关键指标了。
对于一个普通开发者,怎样能做到那20%部分,会涉及多面,譬如:
- 良好的编码习惯
- 扎实的基础
- 抽象思维
- 架构能力
- …
在具体的实现过程,在面对一定复杂度的业务功能时候(简单功能不需要复杂设计,否则本末倒置),如何优雅地实现,方便维护与拓展。
这是你需要考虑的
其实就是架构能力。
架构的本质就是一种指导型约束,以约定整体和部分、部分和部分之间的关系,已使整体更加稳定,更加可靠。(网上资料看到的)
在软件开发中,架构是为了降低开发的复杂度,以一种合理的方式组织代码,通讯等。
在这两年工作中,这方面的能力的确得到了比较好的锻炼。leader会将一个需求给到自己,你就是这个业务或者项目的owner了,至于怎样造是自己的事情,为了避免给自己和其他人留坑,尽量在开发前多想一下:
- 是否需要抽象
- 是否需要分层
- 把量级放大100倍,会不会有性能问题
- ….
那么,在复杂系统的架构都有什么特点?
1.模块化设计。复杂系统一定进行细致的功能、模块、领域的划分。每个模块都有明确,单一的指责。
2.横行+纵向拓展能力。在实现功能上,要考虑未来的功能的承载,预留hook,extends等能力,使得系统有长期价值,而不是通过修修补补打补丁方式实现新功能。
3.分而治之。小的问题得到解决,那么通过合理的依赖和组合,即可有效的解决大的问题,达到整个系统的建设目的。
总结
零零散散记录了一些感悟,一个好的平台在外部环境上的确能给自己带来一个良好的发展空间,在这个基础上,还得靠主观能动性把自己往前推,即在工作的过程中,不断修正自己的定位,找到适合的位置,哪些事情是自己擅长的,把自己所知所学发挥到最大化。
以前的我在项目中追求最前沿的技术栈,不会过多考虑开发成本、维护成本、稳定性等。跨越了5年工作经验的槛,心态上越发觉得技术应该是贴合业务的。好的技术方案应该从业务出发的,甚至是可以驱动业务的。而不是一味追求前沿技术,或者多复杂的技术实现,在成本最低的情况下能够快速解决问题,才是你的leader所希望看到的。
(end.