
二方库维护困境
https://juejin.cn/post/7259331446002925629#heading-16
二方库通常指由团队内自己的需求出发,自己开发的一些lib、sdk、framework。既包括业务小团队开发的公共代码,也包括公司级别内开发的公共代码。
在本篇文章中,这是讨论的主要对象
三方库通常是不由自己团队掌控的代码,比如github上的开源代码。
虽然有些公司级别的公共代码也不由自己团队掌握,但是考虑到这种类型的代码库通常有着非常好的维护、方便的oncall渠道,且是一部分人专职的东西(这是重点!),所以我也分类到了二方库里。
针对业务开发同学,在下面的部分讨论中并不需要区分二方库与三方库,此时会以“二/三方库”这种写法加以区分
所谓困境,就意味着这是一个在比较长时间内,多方诉求都无法得到满足与平衡,以至于大家都怨声不断而又无力改变的一个场景。而一眼可以看出,困境中主要的角色是业务开发同学与二方库开发同学。
二方库开发同学通常会按照roadmap,同时结合各个业务方的功能诉求、性能诉求、bug反馈,不断完善二方库。在这个过程中,二方库开发同学天然会有动力去推动大家升级二方库:
业务开发同学通常会按照排期完成相应feature的开发和bug的修复,而二/三方库的升级并不在工作范围内。面对二/三方库的升级,开发同学通常遇到了这些问题:
从业务开发同学的角度去看,升级并不能总是真的带来收益。常见的升级收益有这些:
遗憾的是,当我们去看一个二/三方库的release note的时候,我们并不是每次都会得到如上信息。有时候是因为release note没有把事情说清楚,有时候是真的这次升级和自己没啥关系。
那么,如果我们升级没有这些收益,我们就不升级了吗?有些二/三方库确实是这样的。但是那些被项目深刻依赖的二/三方库会是例外。被项目深刻依赖的二/三方库在没有确定性收益的情况下,也值得定期升级。首先解释下什么是“被项目深刻依赖的二/三方库”,通常而言,可以称为framework的东西、是业务逻辑的底层二/三方库都是,比如:
其次我想用一个例子来论证被项目深刻依赖的二/三方库在没有确定性收益的情况下,也值得定期升级
代码的功能、性能的优化是没有止境的。业务代码是如此,二/三方库的代码也是如此。有时候我们想实现特定功能,或者做特定优化,就是要依赖新版本的二/三方库(或者依赖二/三方库的生态链工具,而这些生态链工具依赖了新版本的二/三方库)。但是,我们不能等到有这些需求的时候再做二/三方库的升级,因为在此时,服务依赖的版本距离最新的版本已经差太多了,有太多的不兼容改动,升级的风险被累积和放大了。就算是把保障兼容性放在生命信条里的golang,也会因为各种各样的原因作出不兼容的改动:
而如果我们不只是依赖了二/三方库,还fork下来做了一些魔改,那么事情会更加糟糕:我们的修改可能会和最新版本有冲突,而修复冲突又让整个事情麻烦了一重。
在项目立项之初,谁能想到我们未来会根据白名单动态决定某个字段是否需要返回呢?(当我们需要这个功能的时候,距离立项选择grpc已经过去了3年多了)。但是从一个混沌的角度来说,我们未来需要的功能,别人也会需要,而且可能比我们更早需求,并实现了,我们只需要定期升级,就可以享受到别人的成果。而且我相信grpc实现的对我们业务有帮助的功能和优化远不止这些。
因此,如果是一个还在频繁开发的项目,那么外部依赖的二/三方库或是出于功能、或是出于性能,或是出于生态链工具依赖新版本,总是要升级依赖的。其中二方库因为和开发诉求贴合更近,更容易频繁升级。那么迟迟不做依赖升级,等到堆积了足够多的版本才做升级,升级自然变成一个很复杂的事情(长期不升级堆积历史债务)。所以我觉得对于还在频繁开发的项目,不做依赖版本升级是一个没有远见的懒政。
我想特别讨论一下“能用就不要动”这个观点。
首先,服务的稳定性依赖的是系统性的dev ops,而不是“不改代码”。对于一个还在演进的项目,修改的数量并不能与风险的大小画等号,指望通过不改代码就能提高服务的稳定性,是一个很荒谬的事情。
有些同学会担心升级SDK会导致服务出bug,进而被追责,影响绩效,我觉得大可不必担心。如果一个功能重要,那么必然会有完善的回归测试来覆盖,不用担心改代码就会导致重大故障。(当然如果没有dev ops那就QAQemmmm……= =!)而小bug本身不是重点(谁家系统里没点bug呢)。另外,只要代码有变更,就可能会导致升级挂了,那么凭什么二方库的升级导致的事故,比加新功能导致的事故要罪加一等呢?一般来说,只要不是触发红线导致的事故,都不会追责到个人。
所以我们看到,提高服务稳定性的关键是系统性的dev ops。如果一个具有旺盛生命力的项目会信奉“能用就不要动”而不升级依赖,那么说明这个项目的dev ops一定有比较大的问题,其本身质量也堪忧。不过还是要限定一下范围,如果在版本末期,回归测试都快结束了,那么此时暂时不升级依赖,等下个版本再升级也是合理的。
其次,“看任何原则、理念、方法的时候都不能脱离上下文和适用场景,做教条式的理解”。我们刚刚讨论了什么情况下,不应该“能用就不要动”。那么情况下适用“能用就不要动”呢?
如果是一个已经很少有改动的项目,不升级就不升级,问题不大。这类项目通常不只是一个二方库的版本落后了,甚至编译器、官方库、所需要的运行环境都可能已经落后了;甚至情况可以更遭:这个项目没有自动化的单元测试和系统测试,也没有CI/CD(或者有一个非常混乱,有了还不如没有的CI/CD系统),甚至没有测试用例!对这种项目的任何改动,在现代的软件开发方法论和要求中,都需要比较大的精力去完善和实施测试。此外,因为这个项目不频繁改动了,我们并不能享受到二方库升级带来的开发效率提升。
或者,如果是一个已经长期没有新代码的项目,并且没有人对代码足够熟悉,那么“能用就不要动”这个观点也勉强可以成立。
公司还开发了自动升级平台,能够提交mr/pr来更新依赖版本,来简化二方库版本更新。不过这个功能也有局限性。
好吧,在公司有了这么多解决方案之后,还是有这么多吐槽,说明这可能又是一个没有银弹的事情了。我们来浅浅讨论一下可能可以缓解这个问题的一些解决方案吧
这一条是针对二方库开发同学的。业务同学的最主要诉求其实很简单:二方包可以把自己的版本内容讲清楚。因此二方库同学应该在版本管理上下一些功夫。针对版本管理问题,我发现有一些做起来很简单,且能有比较大作用的事情:
对,中文!亲爱的母语!虽然英文看起来很专业,但是在实践中大家的英文表达能力有限,理解能力也有限,通过mr来声明改动面的作用被大大降低了。
另一方面,有时候业务同学不升级二方库的版本是出于信任原因,我常见到这些吐槽:
针对信任问题,我觉得只有不断提高代码的质量、提高覆盖率、长期坚持保证兼容性、有bug及时认错并周知使用方等等方式来不断获取信任。而在细节上,有这些小方法可以帮助你达成上述目标。
对于开发同学而言,并不是每一个开发同学都有意愿去升级二/三方库版本的,免不了不少同学有惰性去依赖老版本。但是站在一个团队、一个项目的角度而言,优质二/三方库的重要性是不言而喻的。有时候替换一个json序列化库就可以带来巨大的性能优化,有时候一个好的二方库可以省去非常多重复编码的时间,有时候依赖的库的老版本爆出来了安全漏洞必须要升级。因此在一个团队内,势必要有一些同学熟悉二/三方库,能够帮助团队做技术选型和底层框架建设。
对于这种团队需求,我的经验是,借鉴外科手术式的研发团队的思路,在团队内部区分出一小批同学来“维护开发工具”(包括IDE、开发插件、二/三方库等等)。这一小批同学也是开发,只是他们会分一部分精力去“维护开发工具”,所以不太需要制定特殊的绩效规则。但是这部分同学需要有比较强的自驱能力或者编程兴趣,能自主地去了解二/三方库、在手头有非关键的项目去试用二/三方库、不定期做二/三方库新版本的分享和升级建议。
我们如果再回顾一下业务开发与二方库开发同学的诉求,就会发现大家最主要的诉求就是自己的付出要有对应绩效的体现。如果没有对应的绩效体现,那么非自己责任内的事情就不做。那么和绩效有关的事情应该找谁解决呢?找老板。
对于二方库同学而言,让大家升级到最新的版本是有天然驱动力的。老板只需要识别到这部分驱动力,给到对应的人力预留即可。比如去建设版本流水线、每个新功能预留足够的时间去保障兼容性、测试覆盖即可。
而对于业务开发的老板而言,就相对麻烦一些了,由浅到深,需要意识到这些事情: