下 做好依赖管理的十五条准则( 二 )


(共 6项)
1、对外部依赖进行封装
不同包的情况,你可能会因为下面因素而考虑是否继续使用它:
基于上述原因,你需要额外工作来方便迁移到新的依赖 。
如果你项目代码中很多地方用到了这个依赖包,那当迁移到一个用于替代它的新依赖包时,你需要修改的地方会很多 。
更麻烦的是,若你提供给外面的 API 包含了依赖包的 API,那当你迁移到新的依赖时,则使用了你 API 的代码都得修改,而那些调用了你 API 的代码,你可能是无法控制的 。
为了避免依赖蔓延而导致的替换成本,你应该
理想情况下,这样可以让你仅需要修改封装器,就能适配不同的依赖包 。
迁移到新的依赖后,记得也修改所以对应的测试用例,以后还可以继续换依赖 。
【下做好依赖管理的十五条准则】在代码搜索系统中,开发了一个抽象的类,类中定义了代码搜索接口,支持任意正则表达式引擎 。

下  做好依赖管理的十五条准则

文章插图
然后为PCRE写了一个轻量的封装器来实现该接口 。这种间接的方式可以更简单的测试不同的库,并可以避免去了解 PCRE 的内部细节 。当然,这也使得我们更容易在不同的库之间切换 。
2、对依赖进行隔离
在程序运行的时候,隔离依赖可避免因为这个依赖有 bug 而导致的崩溃 。
例如,浏览器允许用户添加依赖(即扩展) 。
2008 年发布,引入了一个很关键的功能:将每个扩展插件隔离在运行在独立系统进程的沙箱中 。因此,有 bug 的扩展插件并不能访问到浏览器的全部内存,并可通过系统调用停掉扩展进程 。
在代码搜索系统中,一直将PCRE解释器运行在一个类似的沙箱中,直到弃用它 。现在,可以选择基于轻量级管理程序的沙箱(如) 。将依赖隔离起来可减少很多风险 。
即使有示例,也有现成的选项,在运行时隔离代码依然比较困难,而且的确很少人做到了 。
真正的隔离需要一个完全内存安全的语言,不需要转换为无类型的代码 。这不仅对 C、C++ 等完全不安全的语言有挑战,也对提供受限制的不安全操作的语言提出挑战,如含 JNI 的 Java、和含有「不安全」功能的 Go、Rust、Swift 。
即使在内存安全的语言中如 ,其代码通常也可访问远远超过它所需要的地方 。
NPM 包event-为事件提供流式 API,在其 2018 年 11 月的最新版本中发现了两个半月前添加了混淆过的恶意代码 。这些恶意代码从名为Copay的移动 App 中收集了大量比特币 。这些代码访问了与事件流完全无关的系统资源 。针对此类问题,限制依赖的访问权限是防御措施之一 。
3、如果无法隔离,最好避开
如果某个依赖包的风险太大,而且你也找不到隔离的办法,那最好的办法就是完全不用它 。或至少不要使用有问题的那部分 。
例如,随着谷歌对PCRE风险、成本的更深入了解,在其代码搜索系统中,使用方式也不断发生了变化 。这些变化以下面的顺序发生:
直接使用PCRE
在沙箱中使用PCRE的解释器
写一个新的正则表达式解释器,但依然使用PCRE的执行引擎
写一个新的解释器,并连接到一个不同的,且更高效的开源执行引擎
我们重写执行引擎 。
在重写以后,谷歌对它就没有任何依赖了,我们后来将其开源并命名为RE2
如果你只需依赖包中的一小部分功能,最简单的方法可能是直接复制你所需的那部分下来(当然,也要保留适当的版权和其他法律声明) 。这时你需要自行修复 bug、维护等等,但好处是你可以与较大的风险隔离开来 。