你用 AI coding 工具做出了一个功能。列表显示正常,筛选也能用,再发一句话就能继续修改。可有人问:为什么这一项消失了?改掉这个条件,会不会影响其他筛选?你又得回头问 AI。
拿一个小小的 JavaScript 筛选器试试:先写下它会留下哪些条目,再亲手改库存门槛,运行后看看结果和预测是否一致。
选一个自己能预测的行为
“教会我整个应用”太大了。先选一个能说清结果的问题:类别、搜索文字和“只显示有库存”同时生效时,列表会剩下哪几项?
修改之前,回答三个问题:
- 给定这组输入,应该返回哪些条目的 ID?
- 每个条目被哪条条件留下或排除?
- 如果库存规则变了,哪些结果仍然应该相同?
语法不熟悉时,一次读一个表达式。这个练习中,filter 留下回调返回 true 的条目;&& 要求条件同时满足;|| 两边满足任意一边即可。数据和结果都很小,可以先在纸上算一遍。
下载筛选学习练习。代码和四条数据为本文独立构造,不是抓取的 AI 回答,也不来自真实应用。解压后打开 filter-learning-lab。需要 Node.js 22 或更新版本、文本编辑器和终端;无需安装依赖、调用模型或访问网络。
先写下三个预测,再运行
打开 catalog.mjs 和 filter.mjs。数据只有四条:
| ID | 名称 | 类别 | 库存 |
|---|---|---|---|
folded-stars |
Folded stars | paper |
1 |
paper-kites |
Paper kites | paper |
3 |
clay-moons |
Clay moons | clay |
0 |
clay-bells |
Clay bells | clay |
2 |
原来的库存规则是至少有一件。搜索匹配名称,忽略字母大小写,也去掉查询两端的空白。类别、搜索和库存条件必须同时满足;类别为 all 时接受所有类别,搜索为空时匹配所有名称。
打开 blank-note.md,先写出以下三组输入会留下哪些 ID。可以反复读函数,暂时不要运行观察命令,也别先让 AI 给答案。
| 情况 | 类别 | 搜索文字 | availableOnly |
要说明什么 |
|---|---|---|---|---|
| A | paper |
空 | true |
为什么库存为 1 也符合条件 |
| B | all |
CLAY |
false |
为什么零库存条目仍能出现 |
| C | clay |
bells |
true |
三个条件怎样一起筛选 |
写完后,在解压目录运行:
node observe.mjs
node --test baseline.test.mjs
展开核对原始结果
| 情况 | 原始 ID | 原因 |
|---|---|---|
| A | folded-stars、paper-kites |
都是纸类,库存也都至少为 1。 |
| B | clay-moons、clay-bells |
搜索去掉首尾空白、忽略大小写后匹配两项;库存筛选开关关闭。 |
| C | clay-bells |
类别、名称都匹配,库存为 2。 |
预测不同时,找出自己最先读错的条件。比如 B 不能解释成“这两项都有库存”:clay-moons 的库存是 0。它能留下,是因为 availableOnly 为 false,这个条件没有按库存排除条目。
不借用答案,自己解释一次
如果看过已填写的笔记,先把它关掉。用两三句话向一个正在看数据表的人解释 B,写清实际输入,以及哪条条件允许零库存条目留下。
把几个文件各自的工作分开记:
| 文件 | 在练习里负责什么 |
|---|---|
catalog.mjs |
提供四条数据。 |
filter.mjs |
根据传入条件决定留下哪些条目。 |
observe.mjs |
传入三组条件,打印筛选后的 ID 和库存。 |
baseline.test.mjs |
检查原来的筛选行为。 |
这里的观察器从终端调用函数,没有浏览器界面。换成自己的应用时,输入可能来自按钮或表单;要读调用方,才能知道界面实际传了什么。
可以用 Yenpo 的本地代码阅读工具把数据、筛选函数和调用方一起打开。文件搜索、全文搜索不需要连接模型。配置自己的模型服务商后,也可以向 AI Reader 问一个卡住的条件,再回到附近的源码,自己写出解释。命令由你在 Terminal 中运行,这个练习不需要模型回答。
亲手改一条规则
现在需求变了:打开库存筛选时,只显示足够两个人使用的材料,每人需要一件。类别筛选、搜索文字的处理方式,以及关闭库存筛选时的行为都保持不变。
原来的“至少一件”符合原需求。这次是在改功能,不是在修一个故意藏起来的错误。
动手前再写一次预测。A 应该少掉 folded-stars;B 的开关关闭,仍应包含零库存条目;C 中的 clay-bells 恰好有两件,应该继续留下。
先让新需求的检查跑一次原代码:
node --test exercise/pair-policy.check.mjs
修改之前,这条命令预期会失败,因为下载的函数仍接受库存为 1 的条目。读失败的断言,指出哪项新需求尚未实现。语法错误、找不到文件是另一类失败,不能混在一起。
在 filter.mjs 中,只把库存门槛从 >= 1 改成 >= 2,保留周围的 availableOnly 判断。然后运行:
node observe.mjs
node --test exercise/pair-policy.check.mjs
| 情况 | 修改后的 ID | 检查了什么 |
|---|---|---|
| A | paper-kites |
一件材料不再满足选中的双人规则。 |
| B | clay-moons、clay-bells |
关闭库存筛选时仍不限制库存;搜索仍去除首尾空白、忽略大小写。 |
| C | clay-bells |
恰好两件也符合条件;误改成 > 2 会把它排除。 |
原来的 baseline 测试描述的是一件即可的规则,改变需求后,受影响的断言会失败。新行为用单独命名的双人规则检查来验证,不要为了掩盖失败直接删断言。压缩包保留原始源码,想重做时可以重新解压。
再解释一次,包括没变的部分
暂时合上完成版示例,用自己的话写完笔记。至少说清:
- 原门槛、新门槛分别是什么,改了哪一处。
- 为什么 A 少了一项,B 却没变。
- 为什么 C 中库存恰好为 2 的条目仍应符合条件。
- 跑了哪些命令,各自看到了什么。
- 还没检查什么,例如真实应用的表单输入、服务端行为和数据保存。
具体的解释可以是:“开着库存筛选时,所有条件都要满足,现在库存还必须至少为二。关掉后,库存不影响筛选。因此 A 去掉了 folded-stars,B 和 C 的 ID 不变。”关掉这篇文章,再看着代码讲一次,不用背这段话。
接着换一组输入。先预测结果,再运行,看看自己能否说清每个条目为什么留下或被排除。
回到自己的项目,一次只学一个功能
选一个输入清楚、输出能复现的小行为。记下源码版本,保留副本或使用平时的版本管理方式,第一次只做很小的修改。运行前先写预测;结果不同时,先读相关条件和调用方,再决定是否让 AI 继续生成补丁。
还找不到相关代码时,先看怎样找到陌生代码库的入口。需要判断 AI 的某段解释是否正确,做解释核验练习。正在审查一份改动,则可以看 Promise 缓存补丁的具体审查过程。