节制工程技能:什么时候该写防御性代码、什么时候不该,以及怎么把一轮的构建测试耗时从几十分钟压到几十秒。SKILL.md,可独立使用。
概览
1. ——什么时候该加校验 / try-catch / 抽象层 / 兜底,什么时候不该。 2. ——怎么把构建测试的耗时从几十分钟压到几十秒。 skills/lean-mode/SKILL.md 是标准的 frontmatter(name + description)+ Markdown 正文, ,凡是支持 SKILL.md 约定的 agent 都能用。 大模型写代码时有两种系统性倾向,越到长会话后半段越严重:**和**。下面是从**真实对话记录里量到的数字: :以上全部来自**记录——一个约 20k 行的 Java / Gradle 工程, 模型是 GPT-5.6,一次长会话。它说明这类行为真实存在且量级很大,**跨模型或跨工程 的基准统计,请不要当基准线引用。 这些不是「模型能力不行」,而是**。写进系统提示里的守则,几十轮 之后就被淹没了。 :第一、二、三节的反例给了 四种写法 (同一个错误在不同语言里长得不一样,Go 的「吞 error 返回零值」和 Java 的 catch → return false 是同一件事)。第七节给了 七种构建系统的最小验证回路命令。附录的搜索模式按语言分组。 (Vantaloom:技能页 → 从 URL 拉取)——粘仓库地址即可,会自动在 skills/lean-mode/ 下找到它: :新建一个技能,把 skills/lean-mode/SKILL.md 的内容整份粘进去。
README
lean-mode · 节制工程技能
一份给编码 agent 用的 SKILL.md,回答两个问题:
- 这段「安全」代码值不值得写——什么时候该加校验 / try-catch / 抽象层 / 兜底,什么时候不该。
- 这一轮为什么这么慢——怎么把构建测试的耗时从几十分钟压到几十秒。
它不是「少写代码」的口号。每一条都有明确的判据和明确的例外。
skills/lean-mode/SKILL.md 是标准的 frontmatter(name + description)+ Markdown 正文,
不依赖任何特定宿主,凡是支持 SKILL.md 约定的 agent 都能用。
它在解决什么
大模型写代码时有两种系统性倾向,越到长会话后半段越严重:过度防御性编程和低效 验证循环。下面是从一份真实对话记录里量到的数字:
| 现象 | 实测 |
|---|---|
Objects.requireNonNull |
585 条 / 20740 行,平均每 35 行一条 |
throw new IllegalArgumentException |
454 条,平均每 46 行一条 |
| 两者合计 | 每 20 行主源码就有一条运行时校验 |
| 「操作安全」子系统(租约 / 追踪 / 风险事实 / 超时调度 / 单终态句柄) | 1786 行——而该工程全部核心业务逻辑约 2500 行 |
| Gradle 调用 | 570 次 / 247 分钟(4.1 小时) |
其中带 --no-daemon |
235 次(41%)——每次都付一遍 JVM 冷启动 |
其中带 clean |
55 次——每次丢掉全部增量产物 |
| 跑到 600s 超时被杀掉的命令 | 12 条(约 120 分钟纯浪费) |
| 读文件 : 编辑 | 8381 : 1746(每改一次读 4.8 次),单个文件被重复读 659 次 |
数据来源与边界:以上全部来自同一份记录——一个约 20k 行的 Java / Gradle 工程, 模型是 GPT-5.6,一次长会话。它说明这类行为真实存在且量级很大,不是跨模型或跨工程 的基准统计,请不要当基准线引用。
这些不是「模型能力不行」,而是没有人在每一轮告诉它节制。写进系统提示里的守则,几十轮 之后就被淹没了。
里面有什么
八节 + 一份审计流程:
| 节 | 一句话 |
|---|---|
| 一、校验只在信任边界做一次 | 信任边界只有四处;越过这条线数据就是可信的。判据:这个 null 是从哪来的? |
| 二、不要用 catch 把根因盖住 | catch → return null/false/empty 把一次清晰的崩溃换成一个很难查的错误值 |
| 三、只有一个实现的抽象不是抽象 | 为「将来可能要换」抽的缝,将来八成不在那个位置 |
| 四、修 bug 先改根因 | 加兜底 = 承认没找到根因;必须兜底就注明谁家的问题 + 何时可删,且必须是核实过的 |
| 五、改动量要与行为变化成比例 | 新增 >50 行先报:N 行 / 其中 M 行是校验兜底 / 不写的代价 |
| 六、写「安全代码」前的三问 | 真的会发生吗?发生了用户会怎样?不写的代价是什么?有一问答不上就不写 |
| 七、一轮的耗时是你的责任 | 进程复用 / 增量 / 范围 / 顺序 / 频率 / 超时六条规则 + 七种构建系统的「只跑这一个」 |
| 八、可以说「不用改」 | 产出代码不是目标,把问题解决掉才是;也不要在用户没问的地方顺手「加固」 |
| 附 · 审计已经长歪的代码 | 先量再删:一次搜索量出密度 → 只看最脏的 5 个文件 → 逐条判「删/上移/留」→ 先出表让用户挑 → 再动手 |
语言覆盖:第一、二、三节的反例给了 Java / Go / TypeScript / Python 四种写法
(同一个错误在不同语言里长得不一样,Go 的「吞 error 返回零值」和 Java 的
catch → return false 是同一件事)。第七节给了 Gradle / Maven / Go / Node·TypeScript /
Rust / Python / .NET 七种构建系统的最小验证回路命令。附录的搜索模式按语言分组。
安装
从 URL 拉取(Vantaloom:技能页 → 从 URL 拉取)——粘仓库地址即可,会自动在
skills/lean-mode/ 下找到它:
https://github.com/Timefiles404/lean-mode-skill
放进工作区:
git clone https://github.com/Timefiles404/lean-mode-skill.git
cp -r lean-mode-skill/skills/lean-mode /.vantaloom/skills/
# 使用 .claude/skills/ 约定的宿主同理:
cp -r lean-mode-skill/skills/lean-mode /.claude/skills/
手动:新建一个技能,把 skills/lean-mode/SKILL.md 的内容整份粘进去。
技能靠 frontmatter 里的 description 被按需唤起:正准备加校验、加 try/catch、加抽象层、
加兜底、加「安全」子系统,或者一轮里已经跑了很多次构建测试——这些时刻它会被拉进上下文。
定制
直接改 SKILL.md,它就是一份 Markdown:
- 第七节的命令建议换成你这个工程真实的「只编译」和「只跑一个测试」命令。一条能照抄的 命令远胜一句泛泛的原则。
- 附录的搜索模式按你的代码风格调整(比如你的工程用 Guava
Preconditions还是Objects.requireNonNull)。 - 参考密度值(每 200–500 行一条校验算健康)是经验值,按你的领域调整——写解析器和写 业务 CRUD 的合理密度本来就不同。
边界(不含糊地说清楚)
- 它改变的是模型的倾向,不是模型的能力。 没有任何机制能强制模型不写某种代码—— 技能的全部作用是「在正确的时刻把正确的判据放在它眼前」。有效,但不是保证。
- 它不读你的代码。 这是一份文档,不是静态分析器。附录里的审计流程需要模型自己去跑
那几条
rg命令。 - 判据是启发式的,不是定理。 每一条都带了例外,例外是判据的一部分,别只记结论。
- 数字来自一份记录(见上文的数据来源说明),别当基准线引用。
配套(可选)
Vantaloom 有一个同名插件 lean-mode,把这里的守则做成
每一轮都生效的压力:一份压缩到 718 字节的守则贴在每轮动态上下文末端、工具结果尾注
(本轮第一条失败的命令 / 命令数超阈值 / 同一文件反复改),以及一个把「值不值得」变成
确定性裁决的 lean_review 工具。
没有那个插件,这份技能也完整可用。 分工是:插件负责「每轮都提醒」,技能负责「需要时 给出完整判据」。
许可
鸣谢
推荐工具
换一个关键词,或者移除筛选条件。
安装
npx skillfish add timefiles404/lean-mode-skill