“Catch more React issues in lint.”
这句话,曾经是我贴进项目追踪系统里的整张工单。写法看上去很明确:让 lint 能抓住更多 React 问题。但接下来发生的事情几乎是固定的——一定会有人追过来问:具体是哪些规则?仓库里是不是已经有一套配置?涉及哪些包?我在 Slack 里作答,答案其实早就知道,它们就摊在某个浏览器标签页里,只是从头到尾没有进入过工单系统。
![]()
第二遍写出来的内容总是更好。它有结果,有当时就能拦住提问的那句话。工单系统并没有给我一个空的话题让我发挥,它只是给了一张空白表单,我凭着记忆把它翻译成项目管理系统听得懂的语言,然后顺手丢掉了那些“我觉得显而易见的细节”。丢掉的部分很快又回来了,只不过换了一种形式——一个接一个的追问,在 Slack 里连成一串。
那一串追问,才是我真正该写的工单。
空表单是一道翻译题
一张工单表单,本质上是一组空字段。填写它的过程,是一次翻译工作:你手里本来已经有东西,也许是一个不成句的备忘,也许是一段客户日志,你需要把它重述成看板能接受的说法。这个过程通常发生在仓库没有打开的时候,通常夹在另外两场会议之间。
翻译最容易掉落的,恰恰是那些“太显然了以至于不值得写下来”的部分。Bug 出在哪个界面,通知开关里的“off”到底指什么状态,仓库里是不是已经有一份 Oxlint 配置。这些内容不是提问的人偷懒不看文档,而是它们对填单的人来说太熟了,熟到根本不会意识到自己已经知道。
等这张工单被另一个人捡起来,上面的信息缺口就会变成一连串问题。那个人手里拿着工单,但拿不到你脑子里那套默认假设。写工单的人只能基于自己看得见的东西来写,剩下的部分,只能等提问之后再以消息串的形式补全。
两边都在经历同一个残缺流程
我反过来也经历过同样的过程。打开一张工单,看到一句“catch more React issues”,完全不知道这具体指什么。我去问,然后得到回复,心里甚至会有一点烦。但等我重新看一眼工单,我意识到自己大概率也会提交同样的残稿。作者不是懒,是这张表单要求他从记忆里凭空编出一份规格说明来。
记忆是一次糟糕的结账。它擅长把结论打包,却不擅长把前提一并打包。你清楚知道规则名、插件状态和仓库现状,于是你以为这些信息会自然出现在工单里,但它们不会。它们留在你的脑子里,留在浏览器标签页里,直到有人来问。
- Bug 发生在哪个页面——写单的人以为是显而易见的事。
- 交互里的“关闭”到底关闭了什么——产品逻辑早就刻在作者脑子里。
- 仓库是否已经有对应配置——作者上个月刚看过,于是默认所有人都看过。
这些就是后来会变成消息串的内容。它们不是没有人知道,只是没有被写进应该承载它们的地方。
人和编码代理的差别不在能力,而在重建步骤
一个人类接手工单时,会在动手之前先重建缺失的信息:翻代码、看文档、问同事。这一道步骤在专家那里几乎是无意识的,所以它看起来像是不存在。但一个编码代理经常跳过这个步骤,直接实现一个猜测。等猜测变成代码,问题才以另一种形式暴露出来——你关掉一个你并不想要的 Pull Request。
这中间真正的问题不是谁更聪明,而是流程强迫双方都从残缺的描述出发。人类会靠经验和提问来补全缺口,代理则靠推测和模式匹配来补全缺口。补全的方式不同,失败的方式也不同,但根源是同一条:工单里没写清楚的部分,总会在后面某个环节重新浮上来。
把顺序倒过来才会顺:不要翻译源信息,而是直接保留源信息。让一个模型同时看到源材料和真实仓库的检出状态,由它来起草工单。然后赶在任何其他人之前,先读一遍这份草稿。
一个真实例子:一段话进 Jira,一份草稿出来
我自己在 monorepo 上跑过一次这样的流程。左边那份内容,就是我以前会当作整张工单贴进 Jira 的类型:
“Check oxlint config and available rules from https://oxc.rs/docs/guide/usage/linter/rules.html, we probably need some useful ones related to react, ts, jsdocs”
在这段话下面,我加了一句话:“Let's add useful rules if they are missing.”
翻译成中文,整张工单其实就是这个意思:去查一下 Oxlint 配置和可用规则列表,我们大概需要一些和 React、TypeScript、JSDoc 相关的高价值规则,如果有缺失的规则就补上。这句话听起来像是一个明确的指令,但它其实没有任何可验收的边界:哪些规则算 high-value?缺和不缺的判定标准是什么?改动范围覆盖哪些文件?
右边那份模型草稿,已经完全是另一种对象了。
草稿说出了一张工单该有的信息量
草稿先发现了一个关键事实:monorepo 里并没有提交过的 Oxlint 配置,所以生效的只有默认规则。React 和 JSDoc 插件是关闭状态。TypeScript 有一大批高价值规则,但只有在显式选择开启之后才会生效。这不是一个“把 lint 变得更好”的口号,而是三行字就能说明白的现状。
到这里,工单就不再依赖任何人的记忆了。它不需要有人记得上个月查过配置,不需要有人记得插件默认是关着的,不需要有人记得 TypeScript 规则需要 opt in。这些全部来自对真实仓库的检查,而不是来自作者的浏览器标签页。
接着草稿给出的验收标准,是从对照公共规则列表审计当前用法开始,然后添加一份提交到仓库的配置文件。这已经是一份可以执行的计划了:先审计,再补配置,再提交。每一项都有明确动作,也有明确完成条件。
这才是实际的工作内容,而不是一个像“make lint better”那样的标题。
为什么这看起来像变魔术,其实只是换了一个信源
模型能写出这样的草稿,不是因为模型比填单的人更了解项目,而是因为它的信息来源变了。人类填表时从记忆里提取,模型起草时直接面对源材料和一个真实的仓库检出。它可以把仓库里实际存在的配置、插件状态、规则开关逐项列出来,再把用户需求里那句模糊的“需要一些有用的规则”落成具体的审计和配置动作。
这个流程的价值不在自动化本身,而在于它绕开了翻译损失。你不再需要把一段已经成形的知识从脑子里搬运到表单上,搬运过程中丢掉的部分也不会变成后续那一串追问。模型以源材料为起点,写出的草稿天然带着源头的信息密度。
当然,草稿不一定要直接采用。关键动作是“在任何人读之前先读一遍”。它意味着,在你的想法还没经过别人的提问之前,你已经先用完整的仓库状态重新看了一遍这件事。
读完草稿,再看那行字:“Create Task is still unclick”
这个流程跑完之后,我面对的事实是:Create Task 那个按钮还是没有被点击。也就是说,这份更好的工单并没有直接进入系统,它停在了验证阶段。真正被验证的不是“草稿能不能一键变成工单”,而是“草稿本身是不是已经足够好,好到不需要有人再发一串 Slack 追问来补全”。
这才是整个例子里最有价值的结论。模型草稿的意义不在于替你省掉点击按钮那一下,而在于它提前回答了一堆本来会以“在吗”“请问下”“这个具体指什么”开头的问题。它把那段从记忆里凭空发明规格的压力,从人身上挪走了。
你最后可能还是会手动整理一遍草稿,还是会自己去点 Create Task。但这一次,你不需要再把一个模糊的念头翻译成表单语言。你手上有的是基于真实仓库状态写出的现成文本,你要做的只是读一遍,觉得没问题,然后提交。
下一次打开空白工单时
回到最开头那个重复发生的场景。我写一张工单,写得不够完整,然后在 Slack 里回答追问,补全信息。这条消息串其实就是第二版工单,只不过它没进到该进的地方。
现在我会尝试换一种顺序:先不急着填字段,先保留源材料——那条不完整的笔记
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.