# Notes

> 当用户明确要求补注释、调整注释、review 注释质量、沉淀注释规范，或需要判断复杂逻辑、循环块、if 分支、变量组、状态分支、数据转换、SQL 口径、React Hook 副作用、Node.js 后台编排中哪些位置值得注释时，使用这个 skill。目标是让刚接手模块的人也能读懂注释所说明的意图、变量职责和业务边界，不给简单代码逐行配旁白。

- Skill: `jybill/notes` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add jybill/notes`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jybill/notes/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: jybill (https://skillmd.com/u/jybill)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jybill/notes

---


# notes

这个 skill 用于写代码注释。注释默认写给刚接手当前模块、还不熟悉调用链的人。读者不应先研究完整实现和内部术语，才能看懂注释。

只有任务涉及注释治理时才使用它。普通 React、后端或 SQL 开发任务不应仅因为出现这些技术栈就触发；如果用户要求检查当前改动是否缺少必要注释，可以使用。

## 规范读取要求

通用注释规则已经写在本文件内。执行注释相关任务时，先应用本文件规则，再按场景读取补充文档。

- 涉及前端、React、Hook、组件、事件处理、渲染分支时，再读 `references/frontend.md`。
- 涉及 Node.js 后台、controller、service、SQL、任务、队列、外部接口时，再读 `references/backend.md`。

## 通俗易懂

先用日常动词说明最终效果，再补确实影响理解的原因。不要把代码前提、内部机制、操作对象和最终结果全部塞进一句注释。

- 优先使用“找出、删除、保留、跳过、合并、放到……之前”等具体说法。不要为了显得准确而堆叠只有熟悉代码的人才懂的术语。
- 必须使用技术名词时，只保留代码里确实存在且无法替代的名称，并把它放进完整、简短的句子。
- 不要只写“过滤数据”“遍历列表”这类代码表面动作；说明删除或保留了什么，以及这样做的必要原因。
- 写完按新人视角检查：没有参与实现的人能否看懂数据发生了什么变化、最终留下什么、为什么这样做？不能就重写。

过于复杂：

```ts
// 历史状态可能残留同类消息，下游只消费当前阶段追加的最后一项。
```

过于简单：

```ts
// 过滤消息。
```

推荐写法：

```ts
// 删除旧消息，只保留最新一条，避免重复发送。
```

## 注释决策流程

```text
看到一个代码位置
        │
        ├─ 是导出函数或 class static 方法？
        │        └─ 写简短 JSDoc，说明业务用途、关键入参或返回含义
        │
        ├─ 是 for / if 控制流？
        │        └─ 默认在块上方写一行总结，说明业务产出、过滤边界、状态优先级、状态推进或副作用顺序
        │
        ├─ 是同一逻辑阶段里连续定义多个变量，且后面紧跟 for / if 等逻辑处理？
        │        └─ 每个变量上方写 // 描述变量在后续逻辑里的职责，for / if 上方写逻辑块目标、分支边界或优先级
        │
        ├─ 是复杂判断、状态流转、SQL 口径、异步副作用或模型协议边界？
        │        └─ 写注释说明为什么这样做、避免什么误解
        │
        ├─ 是一组相似字段映射或模板取值？
        │        └─ 在这一组上方写一行总结，不逐字段解释
        │
        ├─ 前面有空行，表示进入新的业务小阶段？
        │        └─ 在这一段代码上方写一行组注释；如果说不出注释，就去掉空行
        │
        └─ 是简单赋值、普通取字段、明显空值回退或普通按钮点击？
                 └─ 不写注释
```

## 通用判断表

| 代码场景                                 | 推荐动作     | 注释重点                                                     |
| ---------------------------------------- | ------------ | ------------------------------------------------------------ |
| 导出函数、class 的 static 方法           | 写 JSDoc     | 写业务用途、关键入参或返回结果的业务含义                     |
| for / if 控制流                          | 默认写组注释 | 说明业务产出、过滤边界、状态优先级、状态推进或副作用顺序     |
| 多变量准备后紧跟 for / if 逻辑处理       | 逐变量注释   | 说明变量在后续逻辑里的职责，逻辑块说明过滤、分支边界或优先级 |
| 复杂判断、连续判断、复杂三元、switch     | 写行内注释   | 写判断依据、状态优先级或分支边界                             |
| 数字计算、聚合统计、字段口径转换         | 写行内注释   | 写计算口径、精度边界、最终展示含义                           |
| 数据结构转换、模板拼接、prompt 边界      | 写行内注释   | 写转换后的读取方式、模型可见边界或用户可见效果               |
| 多行相似取值或字段映射                   | 写一行组注释 | 解释这一组数据的共同语义                                     |
| 空行分隔出的业务小块                     | 写一行组注释 | 说明这一段代码在当前流程里的业务目标                         |
| 简单赋值、普通取字段、明显空值回退       | 不写         | 代码本身已经能表达，不要制造噪音                             |
| “获取某某”“设置某某”“返回某某”“处理某某” | 不写         | 这类注释只复述代码表面行为                                   |

## 规则细节

注释只写在值得解释的地方。它应该说明为什么这样写、避免什么误解、字段代表什么业务含义。简单赋值、普通取字段、明显的空值回退、完全不承载业务含义的语法胶水，不要单独写注释。

多行相似取值如果需要说明，优先在这一组代码上方写一行总结注释，不要每一行都配一句旁白。所有 `for`、`for...of`、`for await...of`、`for...in` 循环默认都要在循环上方写一行组注释，说明本轮循环的业务产出、过滤或归一化边界、状态推进、批量副作用顺序；不要写“遍历某某”这种语法描述。`if`、`else if`、提前返回、`continue` 这类控制流默认也是复杂逻辑入口，只要它在筛选数据、决定状态优先级、保护业务边界、切换副作用顺序或改变后续产出，就要在分支上方写注释说明分支意义；只有一眼可见的空值 guard、普通布尔短路、没有业务含义的语法胶水可以不写。空行只用于分隔不同业务阶段、不同控制流或确实需要停顿的说明。只要保留空行，空行后面的代码块上方就要有一行组注释说明这一小块在做什么；如果这一段简单到写不出有意义的组注释，就去掉空行，让代码连续读下去。

当同一逻辑阶段先连续定义多个变量，后面又紧跟 `for`、`if`、`switch` 等逻辑处理时，不能只用一条组注释覆盖整段。每个变量定义上方都要写 `// ...`，说明这个变量在后续逻辑里的职责，比如基准值、比较对象、累计容器、状态标记、外部协议字段或展示口径；每个 `for` / `if` 逻辑块上方都要写 `// ...`，说明该块负责的过滤、归一化、分支边界、优先级或产出结果。注释仍然不要解释语法本身，也不要写“定义变量”“遍历数组”“判断条件”这类表面描述。

这些要求的重点是补齐复杂流程的阅读入口。agent 不能只给导出函数补 JSDoc 后就跳过内部逻辑，也不能因为代码看起来短就默认没有业务复杂度；只要循环、分支或变量组承载了业务状态、过滤口径、状态优先级或副作用顺序，就要把这层意图写出来。

导出函数、class 的 static 方法，必须在定义上方写简短 JSDoc，说明方法自身职责、关键入参或返回结果的业务含义。不要写只复述函数名的 JSDoc，也不要把通用方法绑定到某个当前调用方场景。

复杂判断、连续判断、复杂三元表达式、switch、数字计算、字段语义、数据结构转换、展示文案拼接、输出模板和 prompt 边界，才是优先补注释的地方。注释要讲判断依据、计算口径、转换后的读取方式、最终用户可见效果或模型可见边界。`for` / `if` 控制流默认要写说明块目的或分支意义的总结；例外只留给完全不承载业务含义的语法胶水。

不要写“获取某某”“设置某某”“返回某某”“处理某某”这种表面注释。不要把十几个字段取值逐行解释。不要写长段落，一条注释只讲一个重点。

## 示例

```ts
/** 生成学生在当前课程范围内的课堂表现摘要。 */
export function buildStudentPerformanceSummaries(records: StudentActivityRecord[], activeCourseIds: Set<number>) {
  // studentSummaryById 是学生维度的累计容器，后续循环持续合并同一学生的课堂表现。
  const studentSummaryById = new Map<number, StudentPerformanceSummary>();
  // absentStudentIds 记录已命中缺勤边界的学生，避免后续互动分数把缺勤状态覆盖掉。
  const absentStudentIds = new Set<number>();

  // 逐条过滤课程范围内的课堂记录，并累计学生维度摘要。
  for (const record of records) {
    // 历史课程记录不进入本轮统计口径，避免跨课程表现混入当前摘要。
    if (!activeCourseIds.has(record.courseId)) continue;

    const summary = studentSummaryById.get(record.studentId) ?? createEmptySummary(record.studentId);

    // 缺勤是最高优先级状态，先锁定学生，再跳过互动分累计。
    if (record.status === StudentActivityStatus.Absent) {
      absentStudentIds.add(record.studentId);
      summary.status = StudentPerformanceStatus.Absent;
      studentSummaryById.set(record.studentId, summary);
      continue;
    }

    // 已缺勤学生只保留缺勤状态，不再用后续互动记录回写为活跃。
    if (absentStudentIds.has(record.studentId)) continue;

    summary.answerCount += record.answerCount;
    studentSummaryById.set(record.studentId, summary);
  }

  return [...studentSummaryById.values()];
}
```

下面这种注释不要写，它只翻译语法，没有降低复杂逻辑的理解成本。

```ts
// 定义学生 Map。
const studentSummaryById = new Map<number, StudentPerformanceSummary>();

// 遍历课堂记录。
for (const record of records) {
  // 判断课程 ID。
  if (!activeCourseIds.has(record.courseId)) continue;
}
```

