SuperAPI AI 工具集

write-discoverable-code

编程与开发

查看项目

更新于 10-16 11:29

技能介绍

用途

write-discoverable-code 是一套编写代码的规则,用于让编码智能体(以及人类)能够通过纯文本搜索找到并理解代码。它适用于任何编写或重命名代码的场景,包括函数、类型、常量、文件、错误消息和文档注释。

其出发点是一条测量结论:智能体靠搜索字符串并阅读命中位置周围的小窗口来发现代码,它们没有悬停提示、没有跳转到定义、也没有跨会话记忆。因此目标是让代码“一次搜索即可定位”,而不是搜索五次。

主要能力与规则要点

1. 名字就是搜索查询

  • 导出的符号使用 2–4 个词的名称,其中至少一个词是领域词。例如用 diffUserObjects 而不是 diff,用 queueEventForDispatch 而不是 queue。在一个约 70 万行的单体仓库上的测量结果是:单词导出名全局唯一率为 61%,三词名为 96%,四词及以上为 98%,三词是曲线拐点。应使用能唯一 grep 到的最短名称,其余信息放进文档注释。
  • 给通用动词加上对象:sanitizeEmailHtml 而非 sanitize,validateSmtpConfig 而非 validateConfig。只限定到满足唯一性所需即可,然后停下。
  • 每个符号只有一个定义处。不要在文件之间复制函数;移动它并在同一次改动中删除原处。共享辅助函数只保留一个以概念命名的家,其余位置通过导入使用。
  • 不要依赖模块路径来消解泛化名称的歧义。区分 users/diff.ts 和 orders/diff.ts 的 import 位于文件顶部,而搜索命中在第 300 行。应把上下文放进符号本身(如 formatDurationMs),而不是文件夹。例外:路径本身承载含义的刚性、绝对约定(例如每个契约文件都导出 Input/Output)。
  • 一个概念,一种拼写。选定 organizationId 或 orgId 并在各处统一使用;每个同义词都会把未来的每次搜索一分为二。应复用正在编辑的代码库中已有的词汇,而不是引入近义词。
  • 当行为或受众改变时,在同一次提交中改名。过时的名字是打开率 100% 的错误信息——这也包括可见性标记:一个已被其他模块导入的 _private 辅助函数需要一个公开名称。
  • 文件名也是名字——绝不要使用裸露角色的文件名。config.ts、types.ts、utils.ts、helpers.ts、handlers.ts 在搜索结果中什么也说明不了,并且会和仓库中其他模块的 config/types/utils 冲突。应加上领域前缀:billing-plan-config.ts,而不是 config.ts。(index.ts 只有在作为薄层再导出入口点时才可接受。)

2. 类型是智能体无法跳过的文档

  • 为原始 ID 加品牌类型:z.string().brand<'UserId'>()(TS)或 newtype(Rust)。transferOwnership(userId: string, orgId: string) 这样的签名会让参数错位变得不可见;品牌类型会让它成为一条点明概念的编译错误。
  • 对特权操作使用能力令牌参数类型(例如要求 OrgScopedDb 而不是裸连接)。注释是一个请求;必需的类型是物理定律。
  • 用可辨识联合建模状态,而不是一堆可空字段加隐含规则。
  • 像它们会被引用回来那样命名类型——它们确实会,出现在智能体用于自我纠正的编译错误里。OrgScopedDb 能自我说明;Ctx2 不能。避免 any:每个 any 都是编译器沉默、智能体重回猜测的地方。

3. 在搜索落地处说明

  • 每个导出都有一行文档注释,说明代码本身无法展示的最关键约束(单位、时区、“来源时间而非插入时间”、所有权)。定义处是名称搜索落地的位置,那一行就是你要传达的全部信息。
  • 在文档注释中写出自然语言短语。搜索以自然语言形式出现(“rate limit”“retry delay”),而 camelCase 标识符不匹配短语 grep——RateLimiter 对“rate limit”的搜索是不可见的。每个导出上方的文档注释应包含有人会搜索的短语,以普通的分词形式出现:SessionExpiryChecker 应写明 /*_ Checks whether the user session has expired. _/,这样 grep “session expired”或“session has expired”就能命中这里。
  • 一个模块应在其 import 未被阅读时也能讲得通。每个被导入的名称加上它的文档行应足以让读者无需打开源模块。如果读者仍需打开,那是 import 的名称在失败,而不是读者。
  • 保持字符串完整。绝不要用模板插值构建事件名、标志或错误码(github.${entity}.${action} 会让 github.pr.merged 无法被搜索)。即使循环看起来更 DRY,也要写出完整字面量。
  • 错误消息以唯一的字面量前缀开头,这样在日志中看到的消息能直接 grep 回抛出点。应写 throw new Error(Webhook signature mismatch for ${id}),绝不要 throw new Error(${prefix}: mismatch)。
  • 每个文件一个可搜索概念,并保持编排器轻薄。回答“X 在哪里完成?”的代码应位于以 X 命名的模块中——读者会问的那个东西,而不是其内部机制——而不是内联在协调器、管道或服务类中。编排器应读起来像一串调用进入命名良好的模块;如果读者从搜索落到其中,每一行都应指向真实实现的一跳之内。把多个概念的实现埋在一个大文件里,会让对其中任何一个的搜索都撞在同一堵代码墙上。拆分到每个问题大小的概念都有一个具名的家,然后停止:只在一个概念内部有意义的辅助函数应内联,而每个微小函数一个文件会把一个答案碎片化到多次阅读中。这个检验是双向的:一个回答许多不相关问题的模块持有的不止一个概念。
  • 就近放置测试(foo.test.ts 放在 foo.ts 旁边),这样一次搜索就能同时找到行为及其规格说明。
  • 标记死胡同。在旧路径上加 @deprecated,并指向新路径。

使用方式

在编写或重命名代码时应用这些规则:函数、类型、常量、文件、错误消息、文档注释。规则文档在提交前提供了一份快速清单:

  1. 对每个新导出名做一次搜索,是否足以找到其实现?
  2. 交换新函数的两个参数是否会构建失败?
  3. 调用者必须知道而签名无法说明的那一件事(单位、时区、所有权、顺序)是否写在了定义处?
  4. 所有日志/错误字符串是否在源码中逐字存在?
  5. 是否有东西改变了行为却没有改变名称?
  6. 代码被移动后,是否已从原处移除?

适用场景

适用于任何以“智能体或人类可通过纯文本搜索找到并理解代码”为目标的代码编写与维护工作,尤其是大型代码库(文档中给出了约 70 万行单体仓库的测量背景)。

限制

关于具体的安装命令、依赖要求、支持的编程语言范围、是否可自动化执行、费用或价格等信息,资料中暂未提供。