如何阅读开源项目代码和我的一些思考
前言:现在可供学习的开源项目不是太少,而是太多。
选择正确的版本和分支
成熟的开源项目通常已经经历过较长时间的迭代,里面的代码结构和概念术语往往相较于最初的版本有很大的变化,迭代的结果往往是不断地抽象,以及不断地兼容不同的环境。简单来说,抽象会导致概念距离具象的实例更遥远,对于用户来说反而更难理解。其次,兼容不同的环境会带来更多的对主干无关的代码,会影响代码的阅读。因此,阅读成熟开源项目代码的一种比较好的方式,就是选择它某个版本(通常选择稳定发布版,开发版本可能会有不稳定的代码)作为起点,可以是早期版本,也可以是最新版本。
选择早期版本的优点在于代码结构相对简单,聚焦的核心问题相对较少,没有为了实现后续需求和解决 Bug 引入的兼容性和优化代码。通过掌握早期版本作为起点的话,可以使用 git log --pretty=format:'%h was %an, %ar, message: %s' > history.log
将 commits 输出到文件中,根据 commit 和关联的 issue、pull request (包括对应社区的讨论)进行总结和归纳。这种跟踪 commits history 的方式会比较细,除非是要对整个项目希望有非常深入的了解,一般可以不用使用,毕竟非常消耗时间和精力,对于大多数人来说,阅读开源项目的首要目的还是为了学习,如果是需要对该项目进行深入的二次开发,可以参考这种方式。还有一种避免追踪过于细节的 commits history 的方法,即跟踪项目的 release notes ,通常在版本发布说明里,会列出每个发布版本新增的一些功能,修复的一些问题,以及做出了哪些优化,这样追踪起来会更加直观、清晰,因为 commits history 里面很可能会有多个 commit 只关联一个功能的情况。
选择最新版本的优点在于缩短跟踪项目演进的时间,上面提到,阅读了早期版本后,往往需要通过跟踪 commits history 或者 release notes 来进一步了解项目演进的情况。其次,可以减少早期概念和设计上的踩坑。实际上,概念模型不一定会随着版本演进而越来越复杂,有的时候可能相反,版本演进会简化概念模型。
灵活使用 git diff
常用的两种情况:
- 只列出两个 commit 不同的文件名:
git diff —name-only SHA1 SHA2
- 比较两个 commit 中的某个文件:
git diff 5790b58b 8e1f8e7a src/raft/proto/raft.proto
这两个命令经常用来比较版本演进的情况。
什么是 Minimal-X ?
Minimal-X 就是对开源项目某个版本进行 code minification (代码简化)。简单来说,就是让该项目只能运行最简单的入门程序,复杂的功能和特性一概移除。比方说,如果是 TensorFlow 或者 Keras ,那么就让它们的代码精简到只能运行 MNIST-CNN 的程度。如果是 Spark 或者 Storm ,那么就让它们的代码精简到只能运行 WordCount 的程度。对项目的代码简化可能会催生一些新项目的思路,比如说针对边缘设备环境的 K3s ,就是对 Kubernetes 某个版本进行了很多的裁剪做出的针对边缘设备的产品。
在阅读开源项目时,精简代码有两种途径——做加法和做减法。加法就是开一个全新的项目目录,从最简单的示例开始,一点点构建出最小覆盖的代码文件集合,边构建边编译、运行。注意,有些代码文件里可能会附带不必要的类或者函数,需要分辨出来并且移除。减法则是在原开源项目的基础上,不断地裁剪代码。加法的优势在于缺什么补什么,在这个过程中可以大致过一遍代码结构,缺点是比较耗费时间和精力。减法的优势则相反,相对节约时间,但是对于大型项目来说做减法的难度会比较大,原因是依赖关系会比较复杂。