如何判断一个技术(中间件/库/工具)的靠谱程度?

Image Title

前段时间周末闲来无事,写了几段小程序。在第三方库上兜了个圈子(悲催地挨个折腾了 pybitcointools, bitcore, libbitcoin, cbitcoin, 挣扎了半天最后又回到pybitcointools),回想起以前看过的 《Game Engine Gems I》的第一篇就是关于这个主题的(“评估和集成中间件的时候应该考虑什么”,"What to Look for When Evaluating Middleware for Integration"),赶紧拿起来翻了翻,顺便总结一下自己的教训,形成文字长点记性。

主观因素

  1. 首先说明的是,不管承不承认,个人品味对选择的影响非常大。即使是同一个程序员,随着视野的开阔和水平的提高,也会不断地推翻自己以前的品味。例子就不举了,这种非常主观的因素不宜多说,知道有这个重要因素在影响着判断,并随时去提醒自己,在技术和工具的选择上不要有过分的成见,傲慢与偏见,俺认为就可以了。

  2. 其次,比普通程序员更爱折腾的文艺程序员,往往还有一个困扰——究竟是该用现成的库,还是自己造轮子呢。如果是自己的 pet project,当然无需多言,怎么折腾都行,甚至换个姿势多来几次都无所谓。但是对于稍微大一点的需要多人协作或控制进度的项目来讲,这个问题就难下定论了。项目类型,成员能力分布,成员熟悉程度,对代码的掌控力,对排期的影响,都是不那么直观的软性待考察因素。

  3. 第三点,很多时候也是最靠谱的一点——朋友的推荐。很多时候我们用一个东西,不是说这个东西有多好,而是那些我们觉得非常靠谱的人(或组织)在用这个东西。这种“安全感”说白了就是一种对他人的经验和判断力的信赖。对创业团队这一点尤其重要,因为跟大公司里按部就班的正规军相比,他们的生存环境要残酷得多,试错机会要少得多。如果不能很好地借力,什么都自己去尝试,风险就会难以控制。

客观因素

说完了这些模糊的,难以度量的主观因素,终于可以说些明确的客观指标了,这也是那篇文章的重点。这里我择要简单说一下,括号里是我加的备注,见谅。

  1. 集成复杂度。好的中间件的特点是高度模块化(就是说不随便暴露无关的接口),最小侵入(普通的使用不需要你使用继承之类的强耦合关系),容易集成到完全不同类型的代码库(比如尽可能地使用可移植代码而不是直接调用平台API),对外部环境有极少的假定,极力与其他系统的实现细节解耦。设计良好的第三方库应尽可能地具有有效的默认行为,需要最小量的配置就可以工作起来。集成的代码接触面积应该最小化(这个接触面积越大越深入,评估周期和维护工作量就会成倍增长)。如果一个中等水平的程序员做了两天还没让集成能初步跑起来,集成复杂度就值得怀疑了。(需要两天以上时间去集成的库,通常需要n个两天去维护)
    (俺认为,配置繁杂、接口凌乱的库,往往意味着作者还没想清楚这个库应该被怎么用,应该用于什么场景,该遮的没遮住,不该露的到处走光,还是很容易识别的)

  2. 内存管理。关心两点:a. 内存占用是否有不合理的开销 b. 内存所有权和分配释放的职责是否清楚。理论上,库的作者应该是对库的内存使用情况最了解的人,他应该定制明确的分配和管理策略。当超出预算时,应该能明确地通知使用者,从而有机会去处理这类事件,而不是任由自己想分多少就分配多少。(更好的设计是把内存使用设计为可伸缩的,这样调用方有机会在运行时根据需要动态去指定内存用量)
    如果一个库不能独立管理一个独立堆或内存池,那至少也要把 alloc/free 的接口开放出来。(这样至少可以接管一下,有机会决定转发到系统堆还是定制的堆,以及插入一些统计和调试性的代码)最糟糕的情况是,直接到处随意地 new/delete。这样的话资源的消耗情况就很难控制了。

  3. 对大量 I/O 访问的处理。硬盘/光盘/网络都是有延迟的,所以访问的策略非常重要。一个第三方库永远不应该直接调用系统API去访问外部资源(如声音文件等),调用方应该有机会去捕获文件和数据的请求,提供定制的方案,从定制的数据源(如已经缓存到内存中的打包数据)读取。最灵活的方式是库完全不提供任何文件或流的读取,只访问给定的内存块,把资源如何获取完全交给调用方开发者处理。最糟糕的中间件,总是假定 POSIX 文件系统在目标平台是有效的,直接依赖C语言运行时的 FILE 和 fopen() 之类的东东。

  4. 日志系统。设计优良的库能够一致地处理运行时的消息,警告和错误,也很容易集成到你已有的日志系统。更好的设计给你一个选项能从高到低(从完全静默到最少量的关键错误到完全的调试信息)逐级开启各类信息。当开启静默的选项时,编译期就能把这些额外的调试输出字符串干掉,以避免额外的开销。当开启 'Noisy verbosity'之类的选项时,系统的各项指标都不断地被输出,通过某段给定的输出,基本能够了解系统当时运行的上下文细节,一些不当的使用也能被及时捕获。

  5. 错误处理,稳定性,性能开销,工具。通常评估一个技术,这些都是必须考察的指标,俺就不一一赘述了。

  6. 客户支持,维护工作量,可移植性等等,这些属于延伸的需求,都很直观,也就略去不提。

最后说一下这里面一条俺认为比较重要的,也是当年带队的MMO项目里,被我列为头条编程规范的原则:绝对,绝对,绝对不要使用没有100%提供源码的第三方技术。这是一条红线,不管这个技术有多强大,都绝对木有例外。程序猿们或多或少都有感触,在编程的世界里,CPU时序的不确定,存储IO的阻塞,其他进程对CPU/内存资源占用造成的扰动,后台进程如杀毒软件偶尔的锁定文件访问,公网路由的拥塞,都为运行着的程序施加了太多不可预知,不可控制的因素。而在这些不可控制的因素里面,允许在自己进程的地址空间内运行一些无法得知其本来面目的代码,是其中最危险也是最容易失控的那一类。反面例子太多,俺就不举了,也免得触物伤怀,影响心境。


基本上主观和客观待考察因素就是这些了,能坚持看到这里,说明您对这个话题确实是感兴趣。为了对得起您的这份耐心,俺特地准备了一点不干不湿的杂货,还请笑纳。


“望,闻,问,切”

在平常的开发活动中,俺总结(山寨)出了“望,闻,问,切”的四字真言,可以用来在一个相对较短的时间里,判断一个技术的适用性。注意,俺说的这些方法,用来鉴别好坏倒还在其次,重点是辨别其是否适用于当下的需求。

“望”

跟传统医学里的驻足远观对方的体态、神态、步态不同,“望”咱们这里取声望口碑之意,也就是间接的评价。上面提过,这里不再细说。总之一句话可以作为底线:“大家都说好的,不一定真的好;大家都说不好的,那基本上好不了。”前半句不解释,后半句是说如果别人都掉到过坑里,那就不要再主动往下跳了。这方面惨痛事例很多,俺也就不举了。

通常这个步骤是不需要花费时间的,一般来源于平日的认识和积累。

“闻”

“闻”可以看作是对其的“第一印象”。正如合格的侦探一眼扫过目标人物就能获取大量的相关信息,训练有素的程序员,从首次了解到某个库的名字和简介,和网站主页面上大致一过,已经可以有一个清晰明确的第一印象了。

俺对一个技术的第一印象通常包括(但不限于):

  • (这个技术) 所试图解决的问题是否清晰,明确。
  • (这个技术) 所依赖的工具/语言/方法是否可靠,通用。
  • Google 搜索第一页的质量情况。(注意,是一整页,不是仅仅官方网站那一条。有时,你会发现除了官方网站,其他条目都是抱怨,那就该“呵呵”了。)
  • 网站的响应速度,专业度和品味。

这个步骤通常时间花费在一到两分钟之内。


通过“望”和“闻”一般就可以决定,是不是需要继续深入去 “问” 和 “切” 了。


“问”

  • 现在开发是否活跃?上一次更新是在什么时候?
  • 有人提交 issue/pull-request 吗?有来自开发者的回复吗?
  • 有明确的 branching model 吗?有发布流程吗?是从master分支发布吗?
  • 是允许 Fork,还是源码下载,还是只有二进制文件?
  • 是否有依赖库?有的话体量有多大,是否为功能所必不可少的?需要单独地维护吗?
  • 是否有简明的 build 步骤?build 工具是你常用常维护的吗?
  • build 流程能够自动化并整合入你的 CI 服务器吗?
  • 对于之前的发布,网站上有汇总的 release note 吗?
  • 对于之后的发布,网站上有清晰的 roadmap 吗?
  • 针对个人/组织,分别是什么 License?如果 License 不同,对功能是否有影响?
  • 信息源(在线文档/教程/作者blog)的质量如何?浮夸吗?样例工程多吗,具体吗?

这些问题虽然看起来略杂乱,但还是能反映一个库的总体质量的,通常五分钟左右就可以有一个基本判断了。

“切”

拿到代码,开始庖丁解牛式地查看,可以称之为“切”。

这个部分其实完全可以独立出一篇文章了——"How to effectively read code",也有一些书是讲这个的,比如 “Code Reading - The Open Source Perspective”之类的,感兴趣的同学可以去翻翻。

题目太大,俺就不展开细说了,只说一个实践之中俺摸索出来的窍门吧:找到这个库最重要的暴露给外部的接口文件(通常是以那个库命名的头文件,如 lua.hzlib.h 等等),就像读文档一样从头到尾(注意,这个顺序很重要)通读一下。看看自己是否能在没什么阻滞的情况下,基本了解这个库的大部分行为。

优秀的接口设计,读起来行云流水,错落有致,当缓处则缓,当急处则急,信息密度均匀,命名平易近人,符合人的直觉和思维习惯,让人读来不费脑力,心情愉悦;而不良的接口设计,读来往往乱作一团,东拉西扯,前后矛盾,概念冲突,甚至于夹三夹四,啰嗦重复,没头没脑,不知所云,或者有悖常识,命名奇葩,又或卖弄学问,哗众取宠,治经弄典,艰深晦涩,搬弄奇技淫巧,极尽冷僻深奥,令人蹙眉扼腕,基本读不下去,自不必提。
(从上面这段话本身结构上的前后对比里,大家应该能感受到区别了吧,呵呵)


对中小规模的技术而言,上面的“望,闻,问,切”已经足以应付了。而对大型代码库/框架/引擎而言,又有一套不大一样的评估标准,另有曲径可探,咱们择日另行讨论,此处暂且按下不表。


写到这里,本文的内容已经基本完整,可以收尾了。不过结束之前,俺来透露一个小秘密吧(思维敏捷的开发者,可能已经想到了):此文明面上为探讨“如何甄鉴一个技术”,实则另有一层涵义——对于程序库的开发者,这其实也是一份可供参考的对照——如果能对上面的视角,方法和手段了然于心,就可以设计出更好,更易用,更为使用者考虑的系统。


看完了俺的介绍,您有什么独特的方法来评估一个技术是否靠谱呢?欢迎在本文后留言,跟大家一起分享和讨论,一定会有更大的收获 : )

[完]
Gu Lu
[2014-07-28]


[2014-07-29] Update,

下面的评论区有同学提到,

可以通过“看github的star數量以及fork的次數”来了解。

俺的回复:

是的,这是一个可资参考的指标。只是要注意,如果说 fork 的次数还能在某种程度上反映出项目的品质和实用价值的话,那 star 数量通常只反映了一个项目的“流行程度”,而流行的东东往往不一定贴合实际项目的需求。须知流行风向会因时而变,因势而变,到那时再去应变,就比较被动了。


[2014-07-29] Update,

@wingc 同学在微博上提到,

反面例子略过不举,但完全符合客观因素六条的第三方库,正面例子来几个哟?有种现实之中很难找出来的感觉... "绝对,绝对,绝对不要使用没有100%提供源码的第三方技术",此说法未必太武断些。软件大厂的库,哪怕没有源码,一般出问题真的是因为没有仔细看文档自己用错了。

俺的回复:

俺觉得"完全符合"的项目,数量上是趋近于零的。通常来讲,技术由于适用面的不同,都会在某种程度上有所折衷。我还是那句话,我们的目标不是寻找最好的,而是通过对这些因素的考察,找到综合来说最适合自己需求的。

设计优良的库,还是不少的,比如俺曾在 blog 上介绍过的 zmq/nanomsg ,但是正如俺开头所说,这玩意受主观影响过大,说多了容易无端被喷,所以先这样吧,日后慢慢介绍不迟。


此外,关于“三个绝对”的问题,俺专门补充说明一下,

  1. 如果所谓的“软件大厂”是第一方,那通常咱们也没啥选择。就比如要在 Windows 上开发 3D 游戏,用闭源的 DirectX 也是理所当然。正如文中所说,所谓“三个绝对”是针对那些几乎总是有得选择的第三方库而言的。

  2. 使用软件大厂的闭源技术,也会带来不小的潜在隐患。大公司通常是不太搭理小团队的,如果你掉进的坑恰好在朋友圈里和网上找不到类似的案例或方案,那尝试跟所谓“软件大厂”交流或反馈一般都是做无用功。最终要么花更多的时间吭哧吭哧workaround,要么去掉对应模块了事。

  3. 作为程序员,当遇到问题时,你希望的是 a) 通过一路在源码中前后追溯,在解决问题之余,弄清楚前因后果,实实在在地增加自己相关领域的经验和认识呢,还是 b) 对着文档反复猜测和校验自己哪个参数有没有误传?

其实到了关键时刻,有代码在手上,就是一颗定心丸,正所谓“源码之前,了无疑惑”。当遇上奇怪的症状时,什么文档都比不上正在运行着的唾手可得的鲜活的代码。

知识共享许可协议
本作品由Gu Lu创作,采用知识共享Attribution-NonCommercial-NoDerivatives 4.0 国际许可协议进行许可。