71 | 如何阅读别人的代码?
前言
为什么要读别人的代码?
如何读懂别人的源码?
读源码的目的又是什么?
注释:本文中的我就是笔者我自己
读别人源码的目的
- 评估是否引入某个第三方模块/库
- 修复某模块的BUG或第三方库的BUG,也有可能要抽离开源框架的部分功能,我目前经常遇到这样的需求
- 学习优秀的开源模块
- 接手或者长期维护某个模块
不搞清楚目标可行吗?
读懂源代码真的很难,它其实是架构的反向过程。它类似于反编译,但是并不是指令级的反编译,而是需要根据指令反推更高维的思想。
注解:
一半的时间消耗在了读懂别人的想法上了,有点像从细胞分析到整个生命体的视角
反编译软件能够将精确软件反编译为汇编,因为这个过程信息是无损的,只是一种等价变换
要让反编译软件能够精确还原出高级语言的代码,这就比较难。
因为编译过程是有损的,大部分软件实体的名字已经在编译过程中被去除了
大部分编译器在编译时会同时生成符号文件。它主要用于 debug 用途。
否则我们在单步跟踪时,debug 软件就没法显示变量的名字。
想像一下,“一个精确还原的智能反编译器” 是怎么工作的
- 它需要识别出所采用的编程语言和编译器。这通常相对容易,一个非常粗陋的分类器就可以完成
- 通过软件的二进制,结合可选的符号文件,加上它对该编译器的套路理解,就可以进行反编译了。
编译器的套路,就如同一个人的行为,持续进行观察学习,是可以形成总结的。
这只需要反编译程序持续地学习足够多的该编译器所产生的样本。
阅读源代码过程一方面是很难的,另一方面来说,也是需要有产出的。
有产出的学习过程,才是最好的学习方式
阅读源代码的产出应该是什么?
答案是,构建这个程序的思路,也就是架构设计
理解架构的核心脉络
看文档
- 有文档,一定要先看文档
- 文档和代码很容易发生脱节。所以我们看到的很可能是上一版本的,甚至是最初版本的设计。
- 阅读过时的架构设计思想对我们理解源代码也会有极大的帮助作用
- 如果发生了冲突,我们需要及时修改文档到与代码一致的版本。
看源码
- 把公开的软件实体(模块、类、函数、常量、全局变量等)的规格整理出来
- 例如,对
Go
语言来说,运行go doc
就可以帮忙整理出一个自动生成的版本。doxygen
也能够做到类似的事情,而且它支持几乎所有的主流语言。
但是这些软件实体各自的业务范畴是什么,它们之间有什么关系?需要进一步分析。
- 先看
example
、unit test
等。这些属于我们研究对象的客户,也就是使用方。它们能够辅助我们理解各个软件实体的语义
我们可以初步推测出各个软件实体的业务范畴,以及它们之间的关系。 - 进一步证实或证伪我们的结论。我们选重点的类或函数,通过看它们的源代码来理解其业务流程,以此印证我们的猜测。如果你能够找到之前做过这块业务的人,不要犹豫,尽可能找到他们并且争取一个小时左右的交流机会,并提前准备好自己遇到迷惑的问题列表。这会大幅缩短你理解整个系统的过程。
- 确保我们正确理解了系统,就需要将结论写下来,形成文档。这样,下一次有其他同学接手这个系统的时候,就不至于需要重新再来一次 “反编译”
理解业务的实现机制
业务系统的概要设计、接口理清楚后,通常来说,我们对这个系统就初步有谱了
在必要的情况下,我们才研究实现机制
需要明确的是,前面我们研究部分核心代码的实现,其目的还是为了确认我们对业务划分猜测的正确性,而不是为了实现机制本身
研究实现是非常费时的,毕竟系统的 UserStory 数量上就有很多。把一个个 UserStory 的具体业务流程都研究清楚写下来,是非常耗时的。如果这个业务系统不是我们接下来重点投入的方向,就没必要在这方面去过度投入。
如果我们只是顺带解决一下遇到的 Bug,无论是用第三方代码遇到的,还是上级随手安排的临时任务,我们自然把关注点放在要解决的 Bug 本身相关的业务流程上
如果我们是接手一个新的业务系统,我们也没有精力立刻把所有细节都搞清楚。这时候我们需要梳理的是关键业务流程。
怎么弄清楚业务流程?
程序 = 数据结构 + 算法
接下来要做的事情是,把这些业务流程相关的数据结构先理清楚。
数据结构是容易梳理的,类的成员变量、数据库的表结构,通常都有快速提取的方式。
除了 MongoDB 可能会难一些,因为弱 schema 的原因,我们需要通过阅读代码的方式去理解 schema。
理清楚数据结构,事情就解决了大半。
剩下来就是理各个 UserStory 的业务流程,并给这些业务流程画出它的 UML 时序图。这个过程随时可以补充。所以我们挑选对我们当前工作最为相关的来做就好了。
同样地,我们要及时把我们整理的结论写下来,变成架构文档的一部分。
这样随着越来越多人去补充完整架构设计文档,才有可能把我们的项目从混沌状态解脱出来
阅读代码的结果,有时不一定仅仅是架构设计文档的补充与完善。我们有时也会顺手修改几行代码。
原则:
- 不做大的改动
- 确保改动前后的语义完全一致
- 不管多自信,有改动就需要补全相关的单元测试,确保修改代码的条件边界都被覆盖。

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
- 上一篇: 70 | 怎么写设计文档?
- 下一篇: 72 | 发布单元与版本管理
目录