heydeer 登录
最佳实践

编写有效的检查

好的检查读起来就像一条新队友通过阅读代码就能应用的规则。关于文件格式和选项,请参阅仓库检查。

每个检查一条可判定的规则

将每条互不相关的规则放在各自的文件中。发现的问题会以检查的文件名标记,而且每个文件都有自己的模式。

把规则写成针对代码的条件:什么会违反它,什么能满足它。

用文件模式限定范围

添加 files 模式,让检查只在规则相关的地方生效;没有模式时,它适用于每个 PR。用 ! 排除测试或测试夹具,并在 YAML 中给每个模式加上引号。

给出理由和示例

说明规则为什么重要,让 HeyDeer 能判断没有预料到的情况。既要描述什么会违反规则,也要描述正确的代码,并附上来自你代码库的简短错误示例和正确示例。

保持检查简短并排好顺序

大多数好的检查一屏就能放下。如果多个检查需要相同的背景知识,请将其移到技能中。

每次审查按文件名顺序最多应用 10 个匹配的检查,因此请给最重要的检查加上数字前缀,例如 10-auth.md。对于简单的检查,设置 skip_extra_reviewers: true;深度审查就会像“标准”那样审查它。参见每次审查最多 10 个检查。

在真实的 PR 上测试

  1. 合并检查。HeyDeer 从基础分支读取检查。
  2. 创建一个违反该规则的 PR,确认有发现的问题指明了你的检查。再确认遵循该规则的代码不会产生任何问题。
  3. 如果检查标记了正确的代码,请收紧规则或模式。如果漏掉了某种情况,请把该情况添加为错误示例。

反模式

反模式为什么行不通改为这样写
含糊:“遵循 API 设计的最佳实践。”没有可以用来衡量代码的条件,因此发现的问题会各不相同。“src/api/ 下的处理函数使用路由的 schema 校验请求体。”
HeyDeer 看不到的内容:“除非 CI 通过且安全团队已批准,否则失败。”HeyDeer 看不到 CI 结果或签核。使用 GitHub 分支规则来要求 CI 和批准。
运行某些东西:“运行测试并报告结果。”HeyDeer 从不运行代码、测试或构建。“对 src/billing/ 的修改需包含一个测试,覆盖被修改的函数。”
格式:“使用两个空格缩进并对导入排序。”格式化工具或 linter 能精确地强制执行这一点。在 CI 中运行这些工具。
推测:“指出任何可能很慢的地方。”没有具体的模式,发现的问题就会变成个人意见。“循环不得为每个元素发起一次数据库查询;应改为批量查询。”
一个文件中包含多条规则:“检查身份验证、日志、命名和测试。”发现的问题共用一个标签,规则也无法分别限定范围。每条规则一个文件,各自有自己的模式。

修改前后对比

修改前:

---
files: "src/**"
---
Follow API best practices. Make sure endpoints are secure, fast,
and well tested, and that naming is consistent. Run the test suite
and fail if anything breaks.

修改后,保存为 .agents/checks/20-api-workspace-scope.md:

---
files:
  - "src/api/**/*.ts"
  - "!**/*.test.ts"
---
# API handlers scope queries to the caller’s workspace

Every query in an API handler that reads or writes a table with a
`workspace_id` column must filter on the workspace returned by
`requireWorkspace()`. Without that filter, one customer can read
or change another customer’s data.

Fail when a handler queries such a table without that filter.
Pass when every such query filters on the workspace, or when the
handler only touches tables without a `workspace_id` column.

Incorrect:

```ts
const invoice = await db.query.invoices.findFirst({
  where: eq(invoices.id, id),
});
```

Correct:

```ts
const { workspaceId } = await requireWorkspace(req);
const invoice = await db.query.invoices.findFirst({
  where: and(eq(invoices.id, id), eq(invoices.workspaceId, workspaceId)),
});
```

改写后的检查只关注一个问题,限定于 API 处理函数,说明什么会失败、什么会通过,并解释风险。其他问题各自成为独立的检查,或交给工具和 AGENTS.md。