Skip to main content
← All posts
作者:Sagasu

#003-文档驱动项目-写出第一份最小可行文档(MVD)

——不求完美,但求有用

系列导读:这是《老项目如何引入文档驱动》系列的第 3 篇。前两篇我们聊了为什么需要文档驱动,以及如何用 AI 理解代码。现在,真正的挑战来了——如何把理解转化为文档?更重要的是,第一份文档该写什么?


小张的困境:文档写到一半就放弃了

小张是我们团队的一名工程师。

上周,他用 AI 分析完支付回调的代码,信心满满地决定:“我要把这些整理成文档!”

结果呢?

他花了整整 3 个晚上,写了 20 页,还没写完一半。

每写一段,他都要纠结:

“这个方法要不要详细说明?”

“这个配置项要不要记录?”

“这段历史演进要不要写?”

第四天,他崩溃了。

他放弃了。


这就是很多人写文档时遇到的困境:想写得完美,结果一点都写不出来。

你可能会说:“那就简单写写不就行了?”

但问题是:简单到什么程度?写什么?不写什么?

这就是这篇文章要解决的问题。

我们不追求完美的文档,而是追求“刚好够用”的文档。

这就是 MVD(Minimum Viable Documentation,最小可行文档)。


一、什么是 MVD?为什么要从“最小”开始?

MVD 的定义

MVD 不是缩水的文档,而是只包含核心信息的文档。

它回答三个最关键的问题:

  1. 是什么(What):这段代码是干什么的?核心流程是什么?

  2. 为什么(Why):为什么这样设计?关键决策是什么?

  3. 怎么用(How - 关键部分):遇到问题怎么办?有什么坑要注意?

不回答的问题:

  • 每个方法的详细实现

  • 所有的配置项说明

  • 完整的历史演进

  • 详尽的数据库设计

这些都很重要,但不是第一优先级。

为什么要从“最小”开始?

1. 降低启动成本

写一份完美的文档需要 20-40 小时。你很难一次性投入这么多时间。

但写一份 MVD 只需要 4-6 小时。这个投入是可接受的。

2. 快速验证价值

MVD 写完就能用,你能立即看到效果:

  • 新人能不能通过这份文档理解代码?

  • 自己 3 个月后回来能不能快速上手?

如果答案是“能”,你就成功了。

如果答案是“不能”,你只浪费了 6 小时,而不是 40 小时。

3. 避免完美主义陷阱

很多人写文档失败,不是因为能力不够,而是因为追求完美。

MVD 的理念是:先有,再好,最后才完美。

第一份文档不需要完美,只需要有用。

对独立开发者来说,MVD 更重要

如果你是独立开发者,同时维护多个项目,MVD 对你的价值更大:

你可能今天在客户 A 的项目,下周切换到客户 B 的项目。

如果没有 MVD:

回来时要花 1-2 天“回忆”当时的设计。

如果有 MVD:

5-10 分钟就能进入状态。

防止知识遗忘:

3 个月后,连自己的代码都看不懂?有了 MVD,关键决策和设计思路都记录在案,随时可以回忆起来。

这就是 MVD 的价值。


二、MVD 框架:老项目该写什么?

对于老项目,我总结了一个三层优先级框架。

第一层:核心流程(必须写)

这是 MVD 的基础。

回答一个问题:这段代码的主流程是什么?

用支付回调举例:

核心流程包含这 7 个步骤:

  1. 接收回调(来自支付网关)

  2. 验证签名(防止伪造)

  3. 查询订单(获取当前状态)

  4. 锁订单(防止重复处理)

  5. 更新状态(根据回调结果)

  6. 释放锁

  7. 返回响应

就这么简单。

时间投入:30 分钟

第二层:关键决策(重点写)

这是 MVD 的灵魂。

回答一个问题:为什么这样设计?

继续用支付回调举例:

2 年前的生产事故:支付宝回调失败重试,导致用户被扣了两次钱。所以加了分布式锁(Redis 实现),确保同一个订单只能被处理一次。

为什么用同步调用而不是异步?

早期用的异步,但发现用户支付后要等 5-10 秒才能看到结果,体验很差。权衡后改成同步,虽然响应时间长了,但用户体验好了。

但它们极其重要,因为它们解释了代码的“合理性”。

时间投入:1-2 小时

第三层:关键细节(选择性写)

这是 MVD 的补充。

不是所有细节都要写,只写容易出错或不符合直觉的部分。

继续用支付回调举例:

幂等性处理:

  • 锁的过期时间是 30 秒,足够处理一次回调

  • 如果处理超过 30 秒,锁会自动释放,可能导致重复处理

  • 所以处理逻辑必须在 30 秒内完成

状态转换规则:

  • PAYING → PAID(正常)

  • PAYING → FAILED(失败)

  • PAYING → CREATED(不允许)

不要尝试回滚到 CREATED,会导致状态混乱。

时间投入:1-2 小时


三、实战:写出你的第一份 MVD

现在,让我们一步步写出支付回调的 MVD。

第一步:准备材料(15 分钟)

在开始写之前,先收集信息:

1. AI 的分析结果

你在第二篇文章中已经让 AI 分析过代码了,把结果拿出来。

2. 代码验证笔记

你验证 AI 分析时记录的发现,比如:

  • AI 遗漏的“锁订单”步骤

  • 实际的状态转换规则

  • 发现的潜在 bug

3. 关键问题列表

问自己几个问题:

  • 为什么这样设计?

  • 有什么坑要注意?

  • 新人最容易搞错什么?

把这些答案记下来。

第二步:搭建文档框架(10 分钟)

一个标准的 MVD 包含这些部分:

  1. 概述 - 一句话说清楚这是什么

  2. 核心流程 - 主流程,7-10 个步骤

  3. 关键决策 - 为什么这样设计?(2-3 个重要决策)

  4. 状态转换 - 列出所有状态和转换规则

  5. 注意事项 - 坑和容易出错的地方

  6. 常见问题 - FAQ,2-3 个

这就是 MVD 的标准框架。

第三步:填充内容(2-3 小时)

技巧 1:先写概述

用一句话说清楚这是什么:

支付回调处理模块负责接收支付网关(支付宝/微信)的异步通知,验证合法性后更新订单状态。

核心职责: 确保支付结果准确同步,防止重复处理。

技巧 2:核心流程用列表

不要写长段落,用列表展示关键步骤:

  1. 接收回调 - 解析请求参数,记录原始数据

  2. 验证签名 - 用支付网关的公钥验证,失败直接返回 400

  3. 查询订单 - 从数据库获取订单当前状态

  4. 锁订单(关键!) - 使用 Redis 分布式锁,过期时间 30 秒

  5. 更新订单状态 - PAYING → PAID(成功)或 FAILED(失败)

  6. 释放锁

  7. 返回成功响应 - 200 OK

每个步骤只需要一句话说明核心动作,不需要展开实现细节。

技巧 3:关键决策讲故事

不要只写“是什么”,讲清楚“为什么”:

为什么要锁订单?

2022 年 8 月,我们遇到一次严重的生产事故。支付宝的回调因为网络问题失败了,它按规则重试了。但我们的代码没有幂等性处理,同一个订单被处理了两次。

解决方案:

引入分布式锁,确保同一个订单在同一时刻只能被一个线程处理。锁的过期时间设为 30 秒,足够处理一次回调。如果超过 30 秒,说明出现异常,自动释放锁。

为什么用同步处理而不是消息队列?

早期设计时,我们用的是消息队列异步处理。但产品经理反馈:用户支付后要等 5-10 秒才能看到“支付成功”,体验很差,用户以为支付失败了,会重复支付。

权衡:

同步处理虽然响应时间长了(200ms → 500ms),但用户体验好了,投诉率下降了 60%。我们接受这个权衡。

技巧 4:状态转换用表格

状态机用表格比文字清晰:

状态转换规则:

当前状态允许的下一状态触发条件
CREATEDPAYING用户点击支付
PAYINGPAID支付成功回调
PAYINGFAILED支付失败回调
PAIDREFUNDING用户申请退款
REFUNDINGREFUNDED退款成功

不允许的转换:

  • ❌ PAYING → CREATED(不要回滚!)

  • ❌ PAID → PAYING(已支付不能回到支付中)

  • ❌ FAILED → PAID(失败不能直接变成功)

技巧 5:注意事项用警告符号

让重要的事情显眼:

⚠️ 锁的过期时间是 30 秒

如果处理逻辑超过 30 秒,锁会自动释放,可能导致重复处理。

所以: 处理逻辑必须在 30 秒内完成。如果有耗时操作,考虑异步化。

⚠️ 幂等性是硬性要求

支付网关可能重试多次,你的代码必须支持幂等性。

测试方法: 用同一个回调数据调用接口 3 次,检查订单状态。

⚠️ 不要在回调中做复杂业务

回调处理应该只做状态更新,不要做:

  • ❌ 发送短信/邮件(放到消息队列)

  • ❌ 更新用户积分(放到消息队列)

  • ❌ 调用其他微服务(异步处理)

原因: 回调必须快速响应(< 1 秒),否则支付网关会重试。

第四步:自我检查(30 分钟)

写完后,不要急着发出去。先自己检查:

检查清单:

  • 概述是否一句话说清楚了这是什么?

  • 核心流程是否清晰?(让不熟悉的人能看懂)

  • 关键决策是否解释了“为什么”?

  • 状态转换是否完整?(没有遗漏)

  • 注意事项是否标注了容易出错的地方?

  • 整体长度是否控制在 2-3 页?(不要太长)

模拟测试:

找一个不熟悉这块代码的同事(或者想象 3 个月后的你),问他:

“看了这份文档,你能理解这段代码吗?”“如果让你改一个 bug,你知道从哪下手吗?”

如果答案是“能”,你就成功了。

最终成果:一份“刚好够用”的 MVD

按照上面的步骤,你最终会得到一份:

特征:

  • 长度: 约 1.5-2 页

  • 耗时: 约 4-6 小时

  • 包含: 概述、核心流程、关键决策、状态转换、注意事项、常见问题

这份文档能做到:

✅ 新人能通过它快速理解核心流程(不需要看代码)✅ 出问题时能快速定位可能的原因✅ 3 个月后的自己能快速回忆起关键决策

这就是 MVD 的价值。


四、常见错误:第一次写文档容易踩的坑

错误 1:写得太详细

症状:

文档写了 20 页,包含每个方法的详细说明、所有配置项、完整的历史演进……

为什么错?

新人不需要这么多信息。太多信息反而让人不知道该看什么。

正确做法:

**只写核心的 20%。 **详细信息等需要时再补充。

错误 2:只写“是什么”,不写“为什么”

症状:

文档只有流程图和方法说明,没有解释设计决策。

为什么错?

看完文档,你知道代码“做了什么”,但不知道“为什么这样做”。

3 个月后,你还是会困惑:“为什么当时这样设计?”

正确做法:

把“为什么”放在和“是什么”同等重要的位置。

错误 3:追求完美

症状:

“这里要不要再详细一点?”“那个方法要不要也说明一下?”“状态机要不要画得更精确?”

结果:永远写不完。

为什么错?

完美是持续改进的结果,不是第一次就能达到的。

正确做法:

先完成,再完美。 第一版只要“够用”就行。

错误 4:文档脱离代码

症状:

写完后跑一遍代码,验证流程是否正确。

为什么错?

文档和代码不一致,导致误导。

正确做法:

写完后跑一遍代码,验证流程是否正确。

错误 5:没有读者视角

症状:

文档写得很技术化,只有自己能看懂。

为什么错?

文档的读者是“不熟悉这块代码的人”。 如果只有你自己能看懂,文档就失去了意义。

正确做法:

找一个不熟悉的同事看一遍,问他能不能看懂。


五、如何判断你的 MVD “够好”?

写完 MVD 后,你可能会困惑:“这样够了吗?”

用这个简单的测试判断:

测试 1:3 分钟理解测试

找一个不熟悉这块代码的同事。

让他花 3 分钟看你的文档,然后问他:

“这段代码是干什么的?核心流程是什么?”

如果他能回答对 70-80%,你就成功了。

测试 2:快速定位测试

假设有个 bug:用户支付成功了,但订单状态还是“支付中”。

看了你的文档,能不能快速定位可能的问题点?

如果能,说明你的文档有实用价值。

测试 3:换位思考测试

假设你是一个刚加入团队的新人。

看了这份文档,你能回答这些问题吗?

  • 这段代码的核心职责是什么?

  • 为什么这样设计?(关键决策)

  • 有什么坑要注意?(容易出错的地方)

如果你能用文档回答这 3 个问题,你就成功了。


额外的验证:

3 个月后,你自己回来看这份文档,能快速回忆起关键决策吗?

这是终极测试,但需要时间验证。

判断标准总结

一份“够好”的 MVD:

  • ✅ 新人能通过它快速理解核心流程(不需要看代码)

  • ✅ 出问题时能快速定位可能的原因

  • ✅ 3 个月后的自己能快速回忆起关键决策

  • ✅ 长度控制在 2-3 页(不超过 5 页)

  • ✅ 投入时间在 4-6 小时(不超过 8 小时)

如果满足这 5 点,你的 MVD 就是成功的。


六、独立开发者的 MVD:更轻量的版本

如果你是独立开发者,同时维护多个项目,可以用更轻量的版本。

轻量 MVD 的三要素

1. 项目概览(2 分钟写完)

## 项目:客户 A 的支付系统

**核心功能:** 支付回调处理

**关键模块:**
- PaymentCallbackHandler(回调入口)
- PaymentStateMachine(状态管理)

**最后更新:** 2024-12-25(新增花呗分期)

2. 核心流程(5 分钟写完)

## 核心流程

接收回调 → 验证签名 → 锁订单 → 更新状态 → 释放锁

**关键点:** 
- 用 Redis 锁防止重复处理
- 锁过期时间 30 秒

3. 关键决策日志(10 分钟写完)

## 决策日志

### 2024-12-25:为什么用同步处理?

用户体验优先。虽然响应慢了,但不用等 5-10 秒。

### 2022-08-15:为什么加锁?

生产事故,用户被重复扣款。加锁解决。

总耗时:约 15-20 分钟

适用场景:

  • 个人项目,主要读者是 3 个月后的自己

  • 时间有限,无法花 4-6 小时写完整 MVD

  • 项目规模小(< 5000 行代码)


七、第一份文档最难,但它是基础

坦白说:第一份文档是最难写的。

因为你不知道该写多详细,不知道该聚焦什么,不知道该省略什么。

我第一次写 MVD 时,也是反复纠结。

写了删,删了写,花了整整 10 小时才写出一份 3 页的文档。

但第一份写完后,后面就越来越容易了。

因为你有了模板,有了参考,知道什么重要什么不重要。

第二份文档只花了 5 小时。

第三份文档只花了 3 小时。

现在,我写一份 MVD 只需要 2-3 小时。


所以,不要追求完美。

先写出第一份,哪怕不完美,哪怕有遗漏,哪怕有错误。

写完了,你就成功了一半。

下一篇,我们会聊:如何让这份文档“活”起来,而不是写完就束之高阁。

文档不是一次性的作品,而是持续演化的知识库。

如何维护?如何更新?如何让它不过时?

这些都在下一篇。


现在就开始吧:

找一个你最熟悉的模块,用 4-6 小时,写出你的第一份 MVD。

不要纠结细节,不要追求完美,先完成再完善。

记住:第一份文档不需要完美,只需要有用。