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个两天去维护)

(俺认为,配置繁杂、接口凌乱的库,往往意味着作者还没想清楚这个库应该被怎么用,应该用于什么场景,该遮的没遮住,不该露的到处走光,还是很容易识别的)

  1. 内存管理。关心两点:a. 内存占用是否有不合理的开销 b. 内存所有权和分配释放的职责是否清楚。理论上,库的作者应该是对库的内存使用情况最了解的人,他应该定制明确的分配和管理策略。当超出预算时,应该能明确地通知使用者,从而有机会去处理这类事件,而不是任由自己想分多少就分配多少。(更好的设计是把内存使用设计为可伸缩的,这样调用方有机会在运行时根据需要动态去指定内存用量)

如果一个库不能独立管理一个独立堆或内存池,那至少也要把 alloc/free 的接口开放出来。(这样至少可以接管一下,有机会决定转发到系统堆还是定制的堆,以及插入一些统计和调试性的代码)最糟糕的情况是,直接到处随意地 new/delete。这样的话资源的消耗情况就很难控制了。

  1. 对大量 I/O 访问的处理。硬盘/光盘/网络都是有延迟的,所以访问的策略非常重要。一个第三方库永远不应该直接调用系统API去访问外部资源(如声音文件等),调用方应该有机会去捕获文件和数据的请求,提供定制的方案,从定制的数据源(如已经缓存到内存中的打包数据)读取。最灵活的方式是库完全不提供任何文件或流的读取,只访问给定的内存块,把资源如何获取完全交给调用方开发者处理。最糟糕的中间件,总是假定 POSIX 文件系统在目标平台是有效的,直接依赖C语言运行时的 FILE 和 fopen() 之类的东东。
  2. 日志系统。设计优良的库能够一致地处理运行时的消息,警告和错误,也很容易集成到你已有的日志系统。更好的设计给你一个选项能从高到低(从完全静默到最少量的关键错误到完全的调试信息)逐级开启各类信息。当开启静默的选项时,编译期就能把这些额外的调试输出字符串干掉,以避免额外的开销。当开启 'Noisy verbosity'之类的选项时,系统的各项指标都不断地被输出,通过某段给定的输出,基本能够了解系统当时运行的上下文细节,一些不当的使用也能被及时捕获。
  3. 错误处理,稳定性,性能开销,工具。通常评估一个技术,这些都是必须考察的指标,俺就不一一赘述了。
  4. 客户支持,维护工作量,可移植性等等,这些属于延伸的需求,都很直观,也就略去不提。

最后说一下这里面一条俺认为比较重要的,也是当年带队的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-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) 对着文档反复猜测和校验自己哪个参数有没有误传?

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

Comments
Write a Comment

Tags

随笔   游戏开发   Programming   C/C++   优化   Unity   C++   知乎   游戏设计   比特币   Unity3D   区块链   软件开发   Bitcoin   引擎设计   系统架构   Production   idtech   中国文化   加密货币   项目管理   游戏评论   资源管理   资源流水线   效率   道德经   网络   方法论   模板编程   Blockchain   Lua   Blockchain Computing   Oculus   GDC   渲染   VR   PerfAssist   Unity MemoryProfiler   BCH   经济学   信息过载   行业报告   字体   Productivity   图形   网络编程   Dice   协程   EMC   Premake   测试   中间件   Game Engine   新手引导   区块链游戏   Methodology   CI   命令行解析   goroutine   ndk   Ethereum   nanomsg   自动化   Scripting   摘录   Debugging   同步技术   cppcon   C++模板   DOOM3   技术评估   Unity GC   C++11   学习方法   Surface Pro 3   Engine Evaluation   CRT   文化   笔记   golang   图形编程   多线程   ETH   Bitcoin Cash   cppcon14   Bitcoin SV   Visual Studio   Unity Coroutine   跨语言可变参数列表   团队协作   货币   Deployment   Visual Assist   工程改进   Michael Abrash   exp   开放世界   量子计算   域名   虚拟现实   系统重构   slua   遮挡剔除   完美转发   协作式调度   Modern C++   类型推导   Memory Debugging   个人成长   小故事   BTC   暴雪   产品   历史   错误处理   Unity Profiler   MOD  

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