最佳实践
编写有效的检查
好的检查读起来就像一条新队友通过阅读代码就能应用的规则。关于文件格式和选项,请参阅仓库检查。
每个检查一条可判定的规则
将每条互不相关的规则放在各自的文件中。发现的问题会以检查的文件名标记,而且每个文件都有自己的模式。
把规则写成针对代码的条件:什么会违反它,什么能满足它。
用文件模式限定范围
添加 files 模式,让检查只在规则相关的地方生效;没有模式时,它适用于每个 PR。用 ! 排除测试或测试夹具,并在 YAML 中给每个模式加上引号。
给出理由和示例
说明规则为什么重要,让 HeyDeer 能判断没有预料到的情况。既要描述什么会违反规则,也要描述正确的代码,并附上来自你代码库的简短错误示例和正确示例。
保持检查简短并排好顺序
大多数好的检查一屏就能放下。如果多个检查需要相同的背景知识,请将其移到技能中。
每次审查按文件名顺序最多应用 10 个匹配的检查,因此请给最重要的检查加上数字前缀,例如 10-auth.md。对于简单的检查,设置 skip_extra_reviewers: true;深度审查就会像“标准”那样审查它。参见每次审查最多 10 个检查。
在真实的 PR 上测试
- 合并检查。HeyDeer 从基础分支读取检查。
- 创建一个违反该规则的 PR,确认有发现的问题指明了你的检查。再确认遵循该规则的代码不会产生任何问题。
- 如果检查标记了正确的代码,请收紧规则或模式。如果漏掉了某种情况,请把该情况添加为错误示例。
反模式
| 反模式 | 为什么行不通 | 改为这样写 |
|---|---|---|
| 含糊:“遵循 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。