
前一篇文章中,我们学习了如何让 AI 帮助拆分一个功能需求。
我们把"商品列表增加搜索和分类筛选"拆成了:
- 页面任务。
- 接口任务。
- 数据任务。
- 测试任务。
拆分需求之后,还有一个很容易被忽略的步骤:
在开始修改代码之前,先让 AI 给出实现方案。
很多初学者拿到任务清单后,会马上说:
text
请根据上面的需求生成完整代码。
这样做不一定错,但很容易出现下面的问题:
- AI 一次修改太多文件。
- 代码结构和项目现状不匹配。
- 方案中的关键假设没有被发现。
- 一个小功能被实现得过于复杂。
- 代码写完后才发现数据结构不合理。
- 出现问题时,不知道应该回退哪一步。
今天我们就来学习一个非常实用的开发习惯:
text
先理解需求
↓
再设计方案
↓
确认方案
↓
最后编写代码
这不是增加无意义的文档工作,而是用几分钟的思考,减少后面几十分钟甚至几个小时的返工。
一、什么是"先出方案,再写代码"
这里说的"方案",不一定是一份很正式的架构设计文档。
对于一个小功能来说,一份合格的方案通常只需要回答下面几个问题:
- 这个功能要解决什么问题。
- 用户会按照什么流程操作。
- 数据从哪里来,保存到哪里。
- 需要修改哪些文件。
- 每个文件分别负责什么。
- 可能出现哪些异常情况。
- 应该按照什么顺序实现。
- 完成后如何验证。
例如,我们有一个待办事项页面,现在希望实现:
刷新页面后,之前添加的待办事项仍然保留。
如果直接写代码,可能马上想到 localStorage。
但真正实现之前,还应该先确认:
- 保存的是全部待办,还是只保存未完成待办。
- 页面打开时什么时候读取数据。
- 添加、完成、删除之后是否都要重新保存。
localStorage中没有数据时使用什么默认值。- 保存的数据损坏时如何处理。
- 是否需要限制待办数量。
方案的作用,就是先把这些问题理清楚。
二、为什么直接写代码容易返工
1. 代码会掩盖需求问题
当 AI 直接输出代码时,很多设计决定已经悄悄被写进去了。
例如:
javascript
localStorage.setItem("todos", JSON.stringify(todos));
这行代码看起来很简单,但它已经默认了:
- 存储键名叫
todos。 todos是一个可以被 JSON 序列化的数组。- 所有待办都保存在浏览器本地。
- 没有考虑数据格式变化。
如果这些决定没有提前确认,后面再修改时就可能需要同时改读取、保存、初始化和删除逻辑。
2. 一次改动太多,问题难以定位
AI 可能一次性修改:
- HTML 结构。
- CSS 样式。
- JavaScript 数据逻辑。
- 工具函数。
- 配置文件。
如果最后页面出现问题,你很难判断:
- 是页面元素没有找到。
- 是数据没有正确读取。
- 是保存时机不对。
- 还是 AI 修改了原本正常的代码。
3. 设计不合理时,后续功能会越来越难加
如果一开始没有想清楚数据结构,后面增加编辑、筛选、排序等功能时,可能需要重新改动大量代码。
方案不一定能保证第一次就完美,但它能让我们提前看见主要的设计选择。
三、一个简单方案应该包含哪些内容
对于初学者,可以使用下面这 8 个部分:
text
1. 需求理解
2. 功能范围
3. 用户操作流程
4. 数据结构
5. 文件和模块改动
6. 异常和边界情况
7. 实现步骤
8. 验收标准
下面逐项说明。
1. 需求理解
先让 AI 用自己的话复述需求。
例如:
text
用户可以在待办事项页面添加、完成和删除任务。
刷新页面后,之前的数据仍然可以恢复。
数据只保存在当前浏览器中,不接入服务器和数据库。
这一步可以帮助我们发现 AI 是否理解错了目标。
2. 功能范围
明确这次做什么,以及暂时不做什么。
例如:
text
本次要做:
- 页面加载时读取本地待办。
- 新增待办后保存数据。
- 标记完成后保存数据。
- 删除待办后保存数据。
本次不做:
- 不接入后端接口。
- 不做多设备同步。
- 不做用户登录。
- 不做数据导出。
范围越清楚,AI 越不容易自行扩展。
3. 用户操作流程
把用户的操作写成步骤:
text
打开页面
↓
读取本地数据
↓
有数据就展示,无数据就展示空列表
↓
用户新增、完成或删除待办
↓
更新页面
↓
保存最新数据
流程图不需要很复杂,重点是让数据变化过程清楚。
4. 数据结构
先确定数据长什么样。
例如:
javascript
[
{
"id": "todo-001",
"title": "学习 AI 编程",
"completed": false,
"createdAt": "2026-08-23T09:00:00.000Z"
}
]
每个字段都应该说明用途:
| 字段 | 类型 | 作用 |
|---|---|---|
| id | string | 唯一标识一条待办 |
| title | string | 待办内容 |
| completed | boolean | 是否已完成 |
| createdAt | string | 创建时间 |
5. 文件和模块改动
提前列出可能修改的文件:
text
可能修改:
- index.html:不需要修改结构,确认已有输入框和列表容器。
- src/todo-storage.js:新增读取和保存本地数据的方法。
- src/todo.js:在新增、完成、删除后调用保存方法。
- src/render.js:继续负责页面渲染,不混入存储细节。
如果是一个简单练习,也可以只有一个 HTML 文件。
重要的是:先让 AI 说明"为什么要改这些文件",而不是让它随意创建文件。
6. 异常和边界情况
至少提前考虑:
- 本地没有数据。
- 本地数据格式不正确。
- 待办内容为空。
- 待办内容只有空格。
localStorage保存失败。- 待办数量过多。
- 数据字段缺失。
不一定所有情况都要在第一版处理,但应该明确哪些要做,哪些暂时忽略。
7. 实现步骤
将方案变成有顺序的步骤:
text
第一步:定义待办数据结构。
第二步:实现读取本地数据的方法。
第三步:实现保存本地数据的方法。
第四步:页面加载时读取并渲染数据。
第五步:在新增操作后保存数据。
第六步:在完成和删除操作后保存数据。
第七步:补充空值和数据格式校验。
第八步:按照验收标准逐项测试。
8. 验收标准
方案最后要说明什么情况下算完成:
text
验收标准:
1. 新增一条待办后刷新页面,待办仍然存在。
2. 标记一条待办完成后刷新,完成状态保持不变。
3. 删除一条待办后刷新,被删除的事项不会重新出现。
4. 输入为空或只有空格时,不新增待办。
5. 本地没有历史数据时,页面可以正常打开。
6. 本地数据格式异常时,页面不会直接崩溃。
四、完整案例:为待办事项增加本地存储
下面用一个简单案例演示如何让 AI 先出方案。
原始需求
text
请给待办事项增加本地存储,刷新页面后数据不要丢失。
这句话仍然不够完整。
我们可以补充项目背景:
text
项目背景:
- 使用原生 HTML、CSS 和 JavaScript。
- 当前已经实现新增、标记完成和删除待办。
- 待办数据保存在 JavaScript 数组 todos 中。
- 页面通过 renderTodos 函数重新渲染列表。
- 本次只使用浏览器 localStorage,不新增第三方依赖。
不推荐的提问方式
text
帮我加 localStorage。
这个提问会让 AI 自行决定:
- 保存逻辑放在哪里。
- 使用什么键名。
- 什么时候读取。
- 什么时候写入。
- 如何处理旧数据。
推荐的提问方式
text
我有一个原生 JavaScript 待办事项页面,
当前已经实现新增、标记完成和删除功能。
当前数据结构是:
[
{
id: "todo-001",
title: "学习 AI 编程",
completed: false
}
]
本次需求:
1. 使用浏览器 localStorage 保存待办数据。
2. 页面打开时读取历史数据。
3. 新增、标记完成和删除后都保存最新数据。
4. 本地没有数据时使用空数组。
5. 本地数据格式错误时不要让页面崩溃。
限制:
1. 不新增第三方依赖。
2. 不修改现有页面样式。
3. 不重写已有的新增、完成和删除逻辑。
4. 先不要写代码。
请先输出:
1. 你对需求的理解。
2. 数据读写流程。
3. 需要修改的函数或文件。
4. 异常情况处理方案。
5. 分步骤实现计划。
6. 可执行的验收标准。
这次 AI 的任务不是"马上写代码",而是先交付一份可以检查的方案。
五、如何审查 AI 给出的方案
拿到方案后,不要直接回复"开始写代码"。
先使用下面 6 个问题检查。
1. 方案是否解决了真正的需求
原始需求是:
刷新页面后数据不要丢失。
方案至少应该包含:
- 页面加载时读取数据。
- 数据变化后保存数据。
- 保存的数据能够在下一次加载时恢复。
如果 AI 只说"新增一个保存按钮",就没有解决真正的问题。
2. 方案是否符合当前技术栈
如果项目是原生 JavaScript,方案却引入 Redux、Pinia 或数据库,就需要让 AI 缩小范围。
可以这样回复:
text
当前只是原生 JavaScript 入门练习,
请删除框架、后端和第三方库相关内容,
只保留浏览器 localStorage 的实现方案。
3. 是否修改了不必要的内容
本次需求是增加数据持久化。
如果方案提出同时重写渲染函数、修改 CSS、替换 HTML 结构,就应该要求 AI 说明必要性。
可以这样问:
text
请把方案中的改动分为"必须修改"和"可以不修改"两类,
并解释每项改动的原因。
4. 数据流是否清楚
需要看清楚数据怎样流动:
text
localStorage
↓ 读取
todos 数组
↓ 渲染
页面列表
用户操作
↓ 修改
todos 数组
↓ 保存
localStorage
如果方案没有说明读取、修改、渲染和保存之间的关系,后面很容易出现页面显示正确但刷新后丢数据的问题。
5. 异常情况是否有处理方式
至少要问 AI:
- 没有数据怎么办。
- 数据不是合法 JSON 怎么办。
- JSON 格式正确但不是数组怎么办。
- 保存失败怎么办。
- 数据中缺少字段怎么办。
不一定所有异常都要复杂处理,但不能完全没有考虑。
6. 每一步是否能够单独验证
例如:
text
实现读取方法后,如何确认读取结果正确?
实现保存方法后,如何查看 localStorage 中的数据?
页面加载恢复后,如何验证完成状态没有丢失?
如果每一步都有验证方法,AI 生成代码后就更容易逐步检查。
六、把方案交给 AI 后,如何分阶段写代码
方案确认后,也不要马上要求 AI 输出整个项目。
可以按照"一个目标、一次验证"的方式推进。
第一步:先实现数据读取
text
请只实现读取 localStorage 的方法。
要求:
1. 使用固定键名保存待办。
2. 没有数据时返回空数组。
3. JSON 解析失败时返回空数组,并保留一个可查看的错误提示。
4. 如果解析结果不是数组,也返回空数组。
5. 不修改页面渲染和用户操作代码。
请先说明实现思路,再给出代码和测试方法。
完成后验证:
javascript
localStorage.removeItem("todos");
console.log(loadTodos());
预期结果是:
text
[]
第二步:再实现数据保存
text
请在现有读取方法的基础上,
只增加保存待办数组的方法。
要求:
1. 接收 todos 数组作为参数。
2. 使用 JSON.stringify 保存。
3. 不修改读取逻辑。
4. 给出一个最小测试示例。
验证方法:
javascript
const testTodos = [
{
id: "todo-001",
title: "测试保存",
completed: false
}
];
saveTodos(testTodos);
console.log(localStorage.getItem("todos"));
第三步:接入页面加载
text
请将 loadTodos 接入页面初始化流程。
要求:
1. 页面加载时先读取本地数据。
2. 将读取结果赋值给现有 todos 数组。
3. 继续调用现有 renderTodos 方法。
4. 不修改新增、完成和删除功能。
5. 说明页面初始化的执行顺序。
第四步:接入用户操作
分别让 AI 处理新增、完成和删除后的保存。
例如:
text
请只修改新增待办的处理函数。
要求:
1. 保留现有的空输入校验。
2. 新增成功后继续调用 renderTodos。
3. 渲染完成后调用 saveTodos(todos)。
4. 不修改完成和删除逻辑。
5. 给出修改前后的关键差异。
完成新增后,再分别处理完成和删除。
第五步:补充异常处理
最后再让 AI 检查:
text
请检查当前 localStorage 持久化实现,
重点关注:
1. 数据为空。
2. JSON 格式损坏。
3. 解析结果不是数组。
4. 待办对象缺少字段。
5. 保存失败。
请只列出风险和测试建议,
暂时不要重写代码。
这种分阶段方式比"一次生成全部代码"更容易理解和回退。
七、方案中要特别关注数据流
很多代码问题,本质上不是语法问题,而是数据流没有设计清楚。
以待办本地存储为例,应该明确下面几个时机:
页面首次打开
text
读取 localStorage
↓
解析数据
↓
校验数据结构
↓
赋值给 todos
↓
调用 renderTodos
新增待办
text
读取输入内容
↓
校验不能是空内容
↓
创建新的待办对象
↓
加入 todos 数组
↓
调用 renderTodos
↓
调用 saveTodos
标记完成
text
找到对应 id
↓
修改 completed
↓
调用 renderTodos
↓
调用 saveTodos
删除待办
text
根据 id 删除数据
↓
调用 renderTodos
↓
调用 saveTodos
如果方案只说明"使用 localStorage",但没有说明这些时机,代码很可能只保存了新增,没有保存完成和删除。
八、什么时候应该让 AI 重新出方案
以下情况出现时,不要急着继续改代码,最好回到方案阶段:
- 需求发生了明显变化。
- 需要更换技术方案。
- 发现原来的数据结构不够用。
- 修改范围从一个文件扩大到多个模块。
- AI 连续给出互相矛盾的建议。
- 修复一个问题导致多个旧功能异常。
- 你已经无法解释当前代码的执行流程。
可以这样要求 AI:
text
当前实现已经和最初方案有较大差异。
请不要继续直接修改代码,
先根据当前代码和新需求重新整理一份方案。
请重点说明:
1. 原方案哪些地方已经不适用。
2. 当前数据流是什么。
3. 哪些文件需要修改。
4. 如何避免影响已有功能。
5. 重新实现的步骤和验收标准。
重新规划不是失败。
当需求、数据或技术约束发生变化时,重新出方案本来就是正常的开发动作。
九、不要把方案设计成过度复杂的架构
先出方案,不代表每个小功能都要设计成大型系统。
初学者尤其要注意"过度设计":
- 只是本地练习,却设计微服务。
- 只有一个页面,却引入复杂状态管理。
- 只有几条数据,却提前设计分布式缓存。
- 只需要一个工具函数,却创建很多抽象层。
- 还没有真实问题,就先增加大量配置。
可以让 AI 遵循一个原则:
text
请优先给出最小可行方案。
只增加当前需求必须的文件、依赖和抽象。
如果存在进阶方案,请单独列出,不要混入第一版实现。
方案的好坏,不是看内容有多复杂,而是看它是否:
- 解决当前问题。
- 容易理解和验证。
- 与项目现状匹配。
- 为后续扩展保留合理空间。
十、一个适合新手的方案确认清单
AI 输出方案后,可以逐项检查:
text
[ ] 我能用自己的话说清楚这份方案要解决什么问题
[ ] 方案符合当前项目使用的语言和框架
[ ] 方案明确了本次做什么和不做什么
[ ] 方案说明了数据从哪里来、到哪里去
[ ] 方案列出了需要修改的文件或模块
[ ] 方案考虑了至少一个异常情况
[ ] 方案没有引入不必要的依赖
[ ] 方案没有修改无关功能
[ ] 每个实现步骤都可以单独验证
[ ] 方案包含明确的验收标准
如果有两三项无法确认,就先不要让 AI 写代码。
可以继续追问:
text
请针对我无法确认的第 [编号] 项,
用更简单的语言解释,并给出一个小例子。
十一、可直接复用的"先方案后代码" Prompt 模板
下面这份模板可以用于大多数功能开发任务:
text
我需要在一个现有项目中开发新功能,
请先出方案,不要直接写完整代码。
项目背景:
- 项目类型:[例如:管理后台、个人练习项目]
- 前端技术:[框架和版本]
- 后端技术:[语言/框架和版本]
- 数据库:[数据库类型,没有则填写"无"]
- 当前相关文件或模块:[文件路径和作用]
需求描述:
[粘贴原始需求]
本次目标:
[明确希望用户最终完成什么]
本次范围:
- 要做:[必须完成的内容]
- 不做:[明确排除的内容]
已知约束:
- [例如:不能新增第三方依赖]
- [例如:兼容当前项目版本]
- [例如:不能修改已有接口]
请按下面结构输出:
1. 需求理解
2. 需要确认的问题和你的最小可行假设
3. 用户操作流程
4. 数据结构和数据流
5. 文件或模块改动清单
6. 异常和边界情况
7. 分阶段实现步骤
8. 每一步的验证方法
9. 最终验收标准
请遵守:
- 不要扩大需求范围。
- 不要虚构不存在的文件和依赖。
- 优先选择简单、容易验证的方案。
- 对不确定的内容明确标注,不要假装确定。
- 等我确认方案后,再开始写代码。
当方案确认后,可以继续发送:
text
方案可以开始实施。
请只完成第 1 步:
[粘贴第 1 步]
要求:
1. 先说明本次会修改哪些文件。
2. 只修改与当前步骤相关的内容。
3. 给出完整代码或最小修改片段。
4. 说明运行和验证方法。
5. 不要提前实现后续步骤。
这段补充 Prompt 可以帮助 AI 控制修改范围,避免一次写出大量未经验证的代码。
十二、总结
"先让 AI 出方案,再让它写代码"是一种很适合新手的设计习惯。
- 方案可以帮助我们先确认 AI 是否理解了需求。
- 方案可以提前暴露数据结构、文件范围和异常处理问题。
- 方案可以把大功能拆成多个可以独立验证的小步骤。
- 方案可以减少一次性大范围修改带来的风险。
- 方案不需要复杂,优先选择简单、匹配当前项目的最小可行方案。
- 方案确认后,再按步骤让 AI 编写代码并逐步测试。
可以把今天的内容浓缩成一句话:
先用方案确认"怎么做",再用代码验证"做得对不对"。
当你养成这个习惯后,AI 就不只是一个代码生成器,而会变成参与需求理解、设计讨论、实现和验证的开发助手。
下一篇文章,我们将继续学习:
《用 AI 生成单元测试:从第一个测试用例开始》
✍坚持原创,求关注,点赞,收藏