首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

哦,那令人讨厌的依赖要怎么管理?

管理依赖的 11 个策略

起先,我只是想写一篇如何提升软件质量的文章。后来在写质量文章的过程中,发现我还差一部分重要的内容没讲:依赖管理。于是,我着手准备写这篇文章,结果发现我还缺少一篇关于:依赖安全机制的文章——依赖孪生。所以,也就有了上一篇文章《依赖孪生:低成本的依赖安全方案》。

笑,为了安装好软件质量这个包,我已经陷入了依赖地狱了。

回到这篇文章,我们将介绍一些依赖管理的相关实践:从基础的依赖环境隔离到告别单体应用。

1. 依赖的环境隔离

事实上,针对于不同项目、不同语言进行环境隔离,本不是这篇文章讨论的范围。但是,我们又不得不提及一些相关的内容。因为,我们总是习惯性也忘记了相关的内容。

由于不同的编程语言的包管理工具,存在对于依赖的 link 机制,所以对于它们的管理也稍有不同:

全局统一依赖型语言的环境隔离。对于诸如 Python、Ruby 这一类脚本型语言来说,它们使用的是全局依赖,所以需要依赖于诸如 或者是 为每个项目创造一个独立的运行时依赖环境。

项目独立的依赖隔离。对于使用 NPM 或者 Yarn 的前端开发人员来说,依赖存在于项目目录下的 ,自然而然地就进行了依赖隔离,但是需要对 Node.js 的版本进行限定。稍微一提,这种机制特别浪费硬盘。

包管理工具版本隔离。对于 Java 的 Gradle 来说,它稍有不同:统一的依赖存储路径,但是却不需要进行环境隔离,不过需要限定 Gradle 的版本。

其它。由于我的语言经验有限,暂时只有这些案例。

:由于经验有限,上述内容有些可能不太准确,如有有错误,欢迎指正。

总之,我们需要针对于不同类型的语言和包管理工具,采用与之匹配的有效的依赖环境隔离。

2. 自建依赖创建

我们已经讨论了太多这方面的内容,这里就不详细展开了。

对于中大型组织来说,可以自建自己的依赖中心。

对于小型组织和团队来说,使用 Git 仓库也是一个不错的折中方案。

3. 进行 LICENSE 扫描

对于依赖的 LICENSE 进行管理和风险识别,是依赖管理的一个重要的部分。除了常见的商用管理方案之外,我们还可以采用诸如于 Node.js 项目中的 https://github.com/mwilliamson/node-license-sniffer 或者是 https://github.com/3rd-Eden/licensing 都是一些不错的工具。

至于 LICENSE 选择的问题,永远都是 MIT 优先。

4. 是否进依赖进行内联?

这是自 left-pad 事件之后,我采取的包的新策略(在我的开源项目上):对于小的软件包,直接复制源码,在项目中创建 放置相关的代码及 LICENSE。

这主要是结合了三个要素而考虑的:

安全性

可迁移性

便利性(因人而异)

当然了内联依赖,涉及到一个版权问题——适合于代码少,不维护的项目。

5. 依赖的协议隔离策略

除了内联之外,我们还要考虑的一个问题就是依赖的协议问题。

策略1:再发布

当我们从 Google 上搜索代码的时候,往往会遇到代码在 、 或者 上,它们非常适合于我们复制到项目中使用,但是难免地可能会遇到一些版权问题。对于开源项目来说,可以直接在项目中使用,并进行标即可。而对于外部项目而言,一种最简单的方式就是二次发布这些软件包。

又或者是对于一些已经不维护的依赖包,而我们又需要,那么就只能在修改后二次发布这些软件包。

策略2:二次协议 / 协议隔离层

对于 JavaScript / TypeScript 类库来说,在这方面的问题比较少,毕竟前端运行在客户端上,所以 Web 前端即『开源』。

如果真的非要使用某个开源软件,并且基于它之上修改,那么可以采取和 Android 开源协议类似的二次协议的方式。Linux 内核采用的是 GPL 协议,而 GPL 具有传染性——如果直接在 GPL 协议的类库上封装,那么只需要开源这部分的代码,因为认为我们的代码并不是衍生产品。而事实上 Free Software Foundation 动态地链接文件也产生了衍生产品,也就是说你用了 GPL 的库,你也需要开源你的代码。

为此,Android 系统在类库上采用的主要是 Apache-2.0 软件许可授权,对 GPL 进行了二次,它允许 Android 上的开发商基于 Android 的源代码进行开发而不向社区反馈。

所以,对协议进行隔离也是一个潜在可行方案,它从某种意义上将风险转移到了这一层级。

6. 依赖更新机制

在日常的业务开发中,最痛苦的一件事莫过于:没有时间做技术升级,没有时间处理技术债。在这个时候,对于依赖更新这个问题,可能就会变得非常痛苦:你可能要亲眼目睹一个遗留系统的形成。所以,作为一个有责任的开发人员,你应该尽量去说服相关的人员,去做好相关的技术更新和升级。

除此,当依赖发现更新的时候,是否有相应的工具可以提供我们?一些现代化的工具,诸如于 Intellij IDEA 可以帮助我们更新依赖。也可以尝试包管理工具自带的升级命令,诸如于 NPM 中的: or 。不过大多数时候,我们可能会放弃升级——除非我们不得不升级。除此,https://david-dm.org 也是一个不错的工具——用于开源的公开 Node.js 项目。

相似的工具还有 next-update 是一个不错的更新工具。又或者是同一个作者写的 next-updater 的工具。

7. 版本选择?宽松型,还是严格型

在引入依赖的时候,我们需要稍微注意一个对于依赖的版本选择问题:到是宽松型,还是严格型。依赖的版本问题,主要会出现在持续集成和新成员加入项目。诸如于使用兼容发布条款,如 ~= 0.6.4,对于那些早期已经搭建了的开发来说,并不会有问题。而新的成员安装的时候,可能就变成了 0.6.5,在 API 上可能会发现一些细微的变化 。而诸如于使用 匹配版本的话,就更不严谨。

以 Python 语言中的 为例,存在不同的描述版本的方式:

一些常见的情况如下:

对于间断型维护的项目来说,往往选择严格型的依赖版本策略,因为大家都不想去碰了,哈哈。

对于长期开发的项目来说,选择宽松型的依赖版本策略,可以让项目可容易维护。

……

8. 使用依赖锁定

为了方便于在不同的开发人员之间能平稳地运行起项目,我们需要使用依赖锁定,以避免出现 DLL 地狱的问题。

DLL 地狱(DLL Hell)指 Microsoft Windows 系统中,因为动态链接库(DLL)的版本或兼容性的问题而造成软件无法正常执行。

为此,我们需要依赖锁定配置文件,来记录已经安装的依赖的确切版本。除此,它还描述了生成的依赖树及其关系,以便于可重现安装依赖(即在后续的安装中能够生成相同的依赖树),而忽略依赖的更新。

依赖锁定配置与依赖配置的最大区别在于,依赖配置文件只描述了需要哪些依赖及期版本,而依赖锁定配置描述了依赖的子子孙孙依赖及期关系。

如我们的一部分依赖是在内部服务器,那么在安装的时候只会从内部服务器拉取相应的依赖,不会去外部环境下载。这个时候,我们所能做的是拷贝依赖,或者是删除这个文件,重新生成。与此同时,这个依赖锁定文件可以通过源码控制工具(如 git)来进行管理。

常见的锁定配置文件有,Ruby 中的 ,Node.js 中的 package-lock.json 或者 yarn.lock。无论你如何修改 的内容,它都会按照 来安装依赖。

9. 依赖代码安全检查

笑~

10. 告别大的单体架构

我的意思并不是说微服务好——不过真的很香,我的主要目的是想说:大的单体应用在依赖上存在着滞后的问题。

对于微服务来说,频繁更新的服务,必然可以做出依赖的及时更新;而对于基本不修改的服务来说,我们基本不会考虑去更新依赖。对于某一长期开发的单体应用而言,它们采用新的构架和编程语言非常困难。因为某个接口的修改,导致要修改一系列的代码,有可能相当于重写整个应用。

诸如于 Spring 框架本身一直在更新,而你正在使用的版本是 2.x,那么你可能与新版本已经不兼容。甚至于,需要维护自己版本的 Spring——出于更新漏洞的原因,你们重写了系统中的部分软件。

11. == 什么是好的依赖

见下一篇文章。

参考内容:

https://github.com/bahmutov/talks/blob/master/dependencies-best-practices-node.md

https://pip.pypa.io/en/stable/user_guide/

https://en.wikipedia.org/wiki/DLL_Hell

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190916A0O8GO00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券