实践Monorepo模式两年多的一些总结

如题所述

团队从19年末开始从multirepo的模式迁移到monorepo,经历了2年多的历程,笔者也有许多感悟。本文主要总结一下自己对monorepo这种项目管理模式的粗浅理解。

本文不会对monorepo的概念进行介绍,如果你还不了解monorepo的概念,可以参考这篇文章:Monorepo是什么,为什么大家都在用?或者其他对于monorepo的介绍文章,网上有很多,一搜一大把。

为什么使用monorepo

当初团队从multirepo迁移到monorepo,主要还是因为方便共享和调试工具库,但是除了这点,monorepo还有其他优势,比如:

便捷共享和调试代码

团队最初使用multirepo最主要的问题是多仓库共享代码、调试非常不方便,也就是开发工具库/组件库时调试很麻烦,每次都需要使用yarnlink的模式来开发,然后开发完成后再进行发版才能供其他项目使用,这就导致了多仓库共享代码成本非常高。

切换成monorepo的模式后,对基础库的开发就非常省事,我们可以直接在项目中引用工具库进行开发调试,不需要再使用yarnlink。

重复的基础建设

除了共享代码繁琐,每个工具库还需要配置自己的基础设施、CI/CD流程、开发环境,同时每个项目都需要专人来维护,这样就很容易导致项目之间的不一致性,而后提升多项目维护成本。

试想一下,如果你需要开发多个项目,而每个项目的开发模式都是不一致的,这种体验是非常糟糕的,也是非常耗费人力成本的。

但是当我们使用monorepo时,我们就可以在使用一套基础建设、开发规范等来降低项目维护的成本。且只需要抽出单独的1-2个人力去专门维护基础建设,其他项目完全不需要再关心。

简化依赖管理

同时因为不再使用的发包模式来管理公共代码,所以对monorepo内的公共依赖都是使用的最新版本,这样就很容易追踪到公共代码的变更会对其他项目的影响,当然这个时候就更需要注重公共代码的自动化测试,因为公共代码只要合并到master后就会立即影响所有线上项目。

快速协助其他项目开发

因为使用了完全相同的基础建设、工作流、开发环境,所以在团队之间相互协助的时候,不需要在配置开发环境、如何部署项目等等流程上重复浪费时间,只需要关心业务的开发即可。

实践过程中遇到问题

使用monorepo的模式的确解决了多仓库联调、基础建设复用等问题,但同时也带来了一些特有的问题。

幽灵依赖(phantomdependency)

幽灵依赖指的是一个库使用了不属于其dependencies里的package,我相信大部分使用monorepo的朋友应该都遇到过或者了解过。

使用未声明的依赖的版本

假设现在APP1依赖了A@1.0.0。突然有一天,APP2也想用A@1.0.0这个依赖,这时,在APP2中使用importXfrom'A'直接跑通了,看起来没什么问题,但其实APP2的?dependencies中并没有添加A依赖,但因为有yarn的依赖提升,将A@1.0.0直接提升到了最外层,让APP2也能使用依赖项A。

后来的某天APP1不需要依赖A了,这个时候它就把依赖项从dependencies中移除了,但是这时候,APP2直接就挂了。

这种case还算是比较好的了,毕竟项目在开发或者部署的时候直接就挂了。

最惨的是,APP1需要升级A@1.0.0到A@2.0.0升级自己的项目回归过,没问题,代码合并到master上线。

APP2这时候就倒霉了,A@1.0.0到A@2.0.0完全不兼容,而APP2完全没有做相关代码的升级,这就导致线上的APP2项目直接就挂了。

peerDependencies错误

看一下这种场景:

APP1依赖A@1.0.0

APP2依赖B@2.0.0

B@2.0.0将A@2.0.0作为peerDependencies,故APP2也应该安装A@2.0.0

但如果APP2没有安装A@2.0.0,那么B@2.0.0则会使用被变量提升的A@1.0.0版本。

如果你运气不错,A@1.0.0和A@2.0.0完全不兼容,那么项目运行就直接挂了。但如果两个版本只是有部分API变更,导致你开发的时候并没有遇到问题,但是一上线,直接就挂了。

或者APP1升级了依赖到A@3.0.0版本,也会导致APP2直接挂掉,因为APP2会使用APP1升级后的依赖。

如何解决

幽灵依赖是一个比较常见的问题,容易导致线上问题是因为比较隐蔽,很难发现,出现问题也很难定位,所以我们需要从根源上杜绝幽灵依赖的问题。

导致问题发生的罪魁祸首就是”依赖提升“,所以我们只需要想办法解决掉它就可以了。

我们可以将包管理器换成pnpm,直接帮我们解决使用依赖但是不写到dependencies中的问题。

编译时间&依赖安装时间变长

这个问题很好理解,1个项目和N+1个项目,哪个编译/依赖安装时间更短?显而易见的是单独1个项目的编译时间更短。

但我们可以通过这两种方式降低构建时间(目前团队正在使用的两种方案):

按需构建减少需要构建的项目

编译时间变长主要是因为需要对所有项目进行构建,但是我们其实只需要构建变更的项目和被变更项目影响到的项目,也就是按需构建。

如果项目使用的是lerna,那么就可以使用yarnlerna:changed找出所有需要构建的项目,然后只对这部分项目进行构建。

手动指定编译项目

当我们不需要将整个monorepo合并到主干分支前,我们可以只对需要构建的项目进行构建。我们可以在commitbody中指定需要编译的项目,在CI脚本中对commitbody进行识别,从而只进行单个项目的构建。

当需要合并的主干分支时,再对整个项目进行构建,只有所以项目都完成CI构建&校验,才能被合并到主干分支。

依赖安装加速

同样,项目变多了之后,依赖项就会非常多,导致安装依赖的时间非常长。

这个解决起来也很容易,如果你使用的是pnpm就可以使用按需安装,也就是使用pnpminstall--filter="XXX"的方式来进行按需安装,相关文档:Filtering。

除了按需安装,还可以通过缓存方案来加速,可以看这篇文章:依赖缓存加速CI构建

Git记录混乱

因为所有人的commit在monorepo里都在一个线性历史里面,所以很乱。如果团队内没有对commit信息进行规范强制校验的话,就会导致项目的commit信息直接废弃。想解决这个问题就需要团队定义好commit规则,加上GitHooks做好前置校验即可。

不过即使做了规范化的处理,也顶不住异常多的commit记录,所以建议使用可视化工具去看Git记录。例如笔者使用的就是IDE自带的Git工具。

项目隐私性和安全性的问题

如果monorepo采用单个仓库,那么所有项目的代码对该仓库的所有开发者都是可见的,如果希望对某一个项目施加细粒度的权限控制,采用单仓库是很难实现的。所以如果你的项目需要做保密处理,那么就需要谨慎选择使用monorepo(当然如果你有别的解决方案也可以告诉我)。

IDE卡顿

实话实说,使用monorepo之后,我的电脑只有四个字:“芜湖起飞”,因为我习惯使用WebStorm/IDEA来开发项目,众所周知JetBrains家的IDE是真的吃性能,而我们的代码仓库又实在太大了,约几十个项目,且每个项目的代码量都不小。

如果你也使用的是IDEA可以通过exclude不需要关注的项目来减缓这个问题,或者只打开自己需要开发的项目。想要彻底解决这个问题的话,建议直接换成M1的Mac。

总结

如果你还没有使用monorepo这种项目管理模式,且可以接受这些痛点的话,非常推荐你进行尝试。尤其是你开发工具库的时候,monorepo的模式实在是太香了。

如果你想实践的话最简单的方式是直接使用yarnworkspace去尝试,毕竟lerna已经停止维护了。可以参考Vue3的实践方式,照抄一下~

以上总结都是个人的一些浅显理解,希望读者朋友能从笔者的总结有所收获。如果你有不同的理解或者建议欢迎在评论区留言指出,非常感谢。

原文:https://juejin.cn/post/7099090192887185439
温馨提示:答案为网友推荐,仅供参考

相关了解……

你可能感兴趣的内容

大家正在搜

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 非常风气网