我最早以为,逆向工程的起点是把代码读懂。
但真正接手一个存量项目之后,我先撞上的并不是某个复杂类或某条调用链,而是一份看起来非常完整的现状文档。
它有模块目录、入口说明、数据关系和流程图。接手者只要顺着目录往下读,很容易相信:当前系统已经被解释清楚,下一步应该直接开始设计理想方案。
可是当我把这份文档和当前实现放在一起对照时,几个很小的偏差开始冒出来:文档里的入口已经变过,配置里的默认值和说明不一致,某个流程在测试里只覆盖了一半,几条“应该如此”的描述却没有任何直接证据。
这些偏差单独看都不大。真正危险的是,它们会被完整的文档结构掩盖。文档越像一份完整说明,接手者越容易把推断当成现实。
所以我后来更认可的逆向工程第一步,不是设计理想结构,而是先记录现实。
接手之前,我先确认它是不是现实
一份文档可以拥有漂亮的目录、统一的标题和完整的章节,但这只能说明它“像一份文档”。
它还需要回答几个更硬的问题:
- 这个入口现在真的存在吗?
- 这条流程现在真的这样运行吗?
- 这个字段是谁产生的,经过了哪些转换?
- 这条规则是当前事实、历史决定,还是作者的推断?
- 如果接手者不相信这句话,应该回到哪里核对?
如果这些问题回答不了,文档再完整,也只是一个容易被误用的假设集合。
相关公开资源
- maglev-reverse-spec:公开的存量项目现实重建能力。
- 逆向现状重建原则:公开说明证据、入口、来源角色和未知项如何分开。
1. 一个“文档看起来完整”的匿名现场
把上面的情况还原成一个更具体的工作画面。
接手者准备修改一个旧业务入口。文档告诉他:这里有一个固定入口,入口下面分成几个模块,数据经过一条已经整理好的流程,最后由测试负责确认结果。
但真正把材料摊开之后,问题不是“少了几页文档”,而是两处关键归因出了偏差:
- 一个控制器入口被记录到了错误的责任边界下;
- 两类相邻的数据/配置操作在文档里使用了混淆的接口标签。
这两个问题都很小,甚至不会让文档立刻看起来崩掉。可一旦接手者按照错误归因开始设计下一版方案,后面的模块边界、调用关系和验证计划都会建立在偏差之上。
我后来把这次逆向过程拆成了三个动作:
| 动作 | 看到的事实 | 处理结果 |
|---|---|---|
| 对照入口 | 文档归属和代码实际入口不一致 | 修正入口责任归属 |
| 对照操作 | 两类相邻操作的标签混淆 | 修正操作标签和边界说明 |
| 对照标准 | 原 Reality 结构完整,但没有充分解释当前状态 | 按当前现实重新组织资料 |
这时最容易犯的错误,是继续补文档,把每个空白都填成一个确定答案。因为只要表格和流程图越来越完整,团队就会产生一种“现状已经被掌握”的错觉。
我后来先做了一个很笨、但更可靠的动作:把每条描述拆成“谁说的、能证明什么、还缺什么”。这一步的结果不是马上得到一份新设计,而是知道哪些地方可以直接依赖,哪些地方必须先验证,哪些地方现在还不能下结论。
2. 三类来源不能互相冒充
逆向过程中,我现在会刻意把来源分开。
意图材料
需求、产品说明或用户描述可以说明:希望解决什么问题、服务什么目标、有哪些约束。
但它不能证明当前实现已经按这个目标运行。
实现材料
页面、接口、代码、配置和数据可以说明:系统现在实际做了什么、对象如何流动、状态如何变化。
但它不能证明用户目标已经成立,更不能自动说明这是一个合理设计。
验证材料
测试、断言、检查脚本和运行记录可以说明:哪些行为已经被检查过。
但通过某个测试,不等于整个生产行为都已经被证明。
如果某类来源缺失,我不会用另一类来源把空白补掉。需求只能说明意图,代码只能说明实现,测试只能说明验证范围。
3. 我现在会按什么顺序重建现实
如果要把这个方法压缩成一条可执行路径,我会按下面的顺序走:
第一步:确定稳定入口
先找到用户目标、页面、接口、任务、命令或事件中最稳定的入口,回答“接手者从哪里开始理解”。
第二步:沿入口追实现和状态
从入口继续追到代码、配置、数据结构、状态变化和错误分支,不停留在目录名或模块名上。
第三步:建立边界账本
记录哪些内容属于这个入口,哪些内容只是相邻能力,哪些内容暂时无法归属。不能为了追求覆盖率创建一个含糊的“其他模块”。
第四步:给每条结论标证据类型
把事实、推断、未知和阻断显式写出来,并为关键判断保留可回查路径。
第五步:先修现实资料,再讨论理想设计
如果发现文档和现实不一致,先修正事实底稿、记录差异和验证依据,再决定哪些问题值得进入下一版设计。
4. 为什么“先记录现实”比“先设计理想”更重要
因为设计会主动填空。
当现状不清楚时,人和 AI 都会根据经验补全缺失部分。补全之后,方案看起来往往比现实更整齐:模块边界更清楚,流程更顺,字段也更完整。
但这种整齐可能只是把未知藏起来了。
先记录现实的价值,是把设计想象和当前事实分开。它允许我们说:
- 这里已经确认,可以作为设计输入;
- 这里只是推断,设计时需要保留弹性;
- 这里还不知道,必须先补证据;
- 这里暂时被权限或基线阻断,不能继续猜。
等这些边界清楚之后,理想设计反而会更快。因为设计不再需要同时承担“理解现实”和“改变现实”两项工作。
结尾:先让接手者知道系统为什么这样运行
逆向工程的第一成果,不是一份看起来完整的文档,而是接手者能够回答:
- 从哪里进入;
- 当前实现做了什么;
- 关键边界在哪里;
- 哪些判断有证据;
- 还有哪些地方不能确定。
我现在越来越觉得,先把现实记录清楚,不是设计之前的低级准备,而是设计能够不从假设开始的前提。
如果一个项目的 Reality 还没有被建立,最应该做的通常不是继续设计更理想的系统,而是先承认:我们还没有足够准确地知道它现在是什么。