bookworm-smart-assistant/skills/reviewer-expert/references/review-checklist.md

250 lines
10 KiB
Markdown
Raw Permalink Normal View History

# 代码审查清单 (Code Review Checklist)
> 按语言和框架分类的审查要点,以及 PR 审查流程 SOP。
---
## TypeScript/JavaScript 审查清单
### 类型安全
- [ ] **禁止裸 `any`**:所有 `any` 必须有注释说明原因,或替换为具体类型 / `unknown`
- [ ] **类型断言最小化**`as` 断言需要注释理由;优先使用类型守卫 (type guard)
- [ ] **泛型正确性**:泛型参数有约束 (`extends`),不使用无约束的 `<T>`
- [ ] **严格模式兼容**`strictNullChecks` 下无隐式 `undefined` 访问
- [ ] **联合类型穷举**`switch` 对联合类型使用 `exhaustive check`never 兜底)
- [ ] **外部数据验证**API 响应、用户输入使用 Zod / io-ts 运行时校验
```typescript
// ❌ 裸 any无校验
function handleResponse(data: any) {
return data.user.name;
}
// ✅ 运行时校验 + 类型推导
const ResponseSchema = z.object({
user: z.object({ name: z.string() }),
});
function handleResponse(raw: unknown) {
const data = ResponseSchema.parse(raw);
return data.user.name; // 类型安全
}
```
### 异步安全
- [ ] **Promise 错误处理**:每个 `await` 都在 `try-catch` 中,或链式 `.catch()`
- [ ] **竞态条件**:组件卸载后不更新 stateAbortController / cleanup function
- [ ] **并发控制**:批量请求使用 `Promise.allSettled` 而非 `Promise.all`(避免一个失败全部丢弃)
- [ ] **超时处理**:网络请求设置合理的 timeout
- [ ] **重试机制**:幂等操作配备指数退避 (exponential backoff)
```typescript
// ❌ 竞态条件:组件卸载后仍设置 state
useEffect(() => {
fetchData().then(data => setData(data));
}, [id]);
// ✅ 使用 AbortController 取消
useEffect(() => {
const controller = new AbortController();
fetchData(id, { signal: controller.signal })
.then(data => setData(data))
.catch(err => {
if (!controller.signal.aborted) reportError(err);
});
return () => controller.abort();
}, [id]);
```
### React 特定
- [ ] **Hooks 依赖数组**`useEffect` / `useMemo` / `useCallback` 依赖完整且无多余项
- [ ] **key 属性**:列表渲染使用稳定唯一 key禁止用 index 作 key排序/删除场景)
- [ ] **不必要的 re-render**:大组件拆分,使用 `React.memo` / `useMemo` 防止子树重渲染
- [ ] **状态提升 vs 下沉**state 放在最近的共同祖先,不过度提升到顶层
- [ ] **Context 粒度**:避免巨大 Context 导致不相关组件重渲染
- [ ] **受控 vs 非受控**:表单组件模式一致,不混用
### Next.js 特定
- [ ] **Server / Client 边界**`'use client'` 声明仅在必要时添加Server Component 优先
- [ ] **数据获取位置**Server Component 中直接 fetch不在 Client Component 中请求可在服务端获取的数据
- [ ] **缓存策略**`fetch` 的 `cache` / `revalidate` 选项合理;理解 ISR vs SSR vs SSG
- [ ] **Metadata**:页面导出 `metadata``generateMetadata` 以支持 SEO
- [ ] **Loading / Error 边界**`loading.tsx` 和 `error.tsx` 文件完整
- [ ] **Route Handler 安全**API route 有鉴权中间件,参数经过校验
---
## Python 审查清单
### 类型标注
- [ ] **typing 模块使用**:函数签名完整标注参数和返回值类型
- [ ] **Pydantic model 验证**API 入参使用 Pydantic BaseModel字段有 `Field` 约束
- [ ] **Optional 显式标注**:可为 None 的字段使用 `Optional[T]``T | None`
- [ ] **泛型容器**:使用 `list[str]` 而非 `List`Python 3.9+
- [ ] **TypeAlias**:复杂类型定义使用 `TypeAlias` 提高可读性
```python
# ❌ 无类型标注,无验证
def create_user(data):
return db.execute(f"INSERT INTO users VALUES ({data['name']})")
# ✅ Pydantic 验证 + 类型标注
class CreateUserRequest(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
email: EmailStr
async def create_user(request: CreateUserRequest) -> UserResponse:
return await user_service.create(request)
```
### FastAPI 特定
- [ ] **依赖注入**:数据库 session、当前用户等通过 `Depends()` 注入
- [ ] **错误处理**:使用 `HTTPException` 返回合适状态码;全局异常处理器兜底
- [ ] **中间件链**CORS、日志、限流中间件顺序正确
- [ ] **路由组织**:使用 `APIRouter` 按模块分组;路径命名 RESTful
- [ ] **响应模型**`response_model` 显式声明,避免泄露内部字段
- [ ] **后台任务**:耗时操作使用 `BackgroundTasks` 或 Celery
### 异步安全
- [ ] **asyncio 使用**:不在 async 函数中调用同步阻塞 I/O使用 `run_in_executor`
- [ ] **连接池管理**数据库连接池大小合理async session 正确关闭
- [ ] **并发限制**:使用 `asyncio.Semaphore` 控制并发数
- [ ] **上下文变量**:使用 `contextvars` 而非全局变量传递请求级数据
---
## Go 审查清单
### 错误处理
- [ ] **error wrapping**:使用 `fmt.Errorf("context: %w", err)` 包装错误,保留链路
- [ ] **sentinel errors**:预定义错误使用 `errors.New` 并以 `Err` 前缀命名
- [ ] **自定义 error**:复杂错误实现 `Error()` 接口,携带结构化上下文
- [ ] **errors.Is / As**:错误判断使用 `errors.Is` 而非 `==`
- [ ] **不忽略错误**`_ = someFunc()` 必须有注释说明原因
```go
// ❌ 错误被忽略,无上下文
data, _ := json.Marshal(user)
db.Exec("INSERT INTO users VALUES (?)", data)
// ✅ 错误包装,逐层传递
data, err := json.Marshal(user)
if err != nil {
return fmt.Errorf("序列化用户数据失败: %w", err)
}
if _, err := db.Exec("INSERT INTO users VALUES (?)", data); err != nil {
return fmt.Errorf("插入用户记录失败: %w", err)
}
```
### 并发安全
- [ ] **goroutine 泄漏**:每个 goroutine 有明确退出条件context cancel / done channel
- [ ] **mutex 使用**:共享数据用 `sync.Mutex``sync.RWMutex` 保护;锁粒度合理
- [ ] **channel 模式**:有缓冲 vs 无缓冲选择合理;`select` 配 `default``context.Done()`
- [ ] **sync.WaitGroup**`Add` 在 goroutine 外调用;`Done` 使用 `defer`
- [ ] **data race**:运行 `go test -race` 检测竞态
### Gin 特定
- [ ] **中间件**鉴权、日志、Recovery 中间件顺序正确
- [ ] **参数绑定**:使用 `ShouldBindJSON` / `ShouldBindQuery` 而非手动取值
- [ ] **响应格式**:统一使用 `c.JSON()` 返回标准格式 `{ code, message, data }`
- [ ] **路由分组**`v1 := r.Group("/api/v1")` 按版本和模块分组
- [ ] **优雅关停**:使用 `signal.Notify` + `srv.Shutdown(ctx)` 处理退出
---
## PR 审查流程 SOP
### 审查前准备
1. **理解上下文**:阅读 PR 描述和关联 issue明确变更目的
2. **检查 CI 状态**确认自动化测试通过、lint 无报错
3. **浏览变更范围**:先看 file changes 概览,判断影响面
4. **本地检出**(可选):复杂变更建议本地运行验证
### 审查步骤(按优先级)
```
Step 1: 架构审查
- 变更是否符合系统架构设计?
- 模块职责划分是否合理?
- 依赖方向是否正确(不反向依赖)?
Step 2: 逻辑正确性
- 业务逻辑是否正确?边界条件是否覆盖?
- 数据流转是否完整?状态管理是否一致?
Step 3: 安全检查
- 输入是否经过验证和清理?
- 是否存在注入、XSS、CSRF 风险?
- 敏感数据是否加密 / 脱敏?
Step 4: 性能评估
- 是否引入 N+1 查询、大数据量循环?
- 缓存策略是否合理?
- 数据库索引是否匹配新查询?
Step 5: 代码风格
- 命名是否清晰、符合团队约定?
- 代码组织是否合理?是否过度抽象?
```
### 反馈规范
- **区分严重度**每条反馈标注级别Blocker / Warning / Suggestion / Nit
- **提供替代方案**:指出问题的同时给出建议的修改代码
- **解释原因**:说明为什么需要修改,而不是只说"改一下"
- **肯定优点**:发现好的设计和实现也要给予正面反馈
- **对事不对人**:审查代码而非审查人,使用"这段代码"而非"你的代码"
---
## 严重度分类标准
| 级别 | 标记 | 定义 | 示例 | 处理要求 |
|------|------|------|------|----------|
| **Critical** | `[BLOCKER]` | 必须修复才能合并。存在正确性、安全性或数据完整性问题 | SQL 注入漏洞;竞态条件导致数据不一致;未处理的空指针异常 | 必须修复后重新审查 |
| **Warning** | `[WARNING]` | 强烈建议修复。存在性能隐患、可维护性问题或潜在 Bug | N+1 查询问题;缺少错误处理;过大的函数需要拆分 | 应当修复,可协商延期 |
| **Suggestion** | `[SUGGESTION]` | 改进建议。提升代码质量但不影响功能 | 可以使用更清晰的命名;建议提取公共方法;增加类型约束 | 可选,鼓励采纳 |
| **Nit** | `[NIT]` | 细微问题。风格偏好或微小优化 | 空行数量import 排序;注释措辞 | 可选,不阻塞合并 |
### 审查反馈模板
```markdown
**[BLOCKER]** SQL 注入风险
- 文件: `src/services/user.ts:42`
- 问题: 用户输入直接拼接到 SQL 查询字符串
- 建议:
```typescript
// 使用参数化查询
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
```
**[SUGGESTION]** 建议提取复用函数
- 文件: `src/utils/format.ts:15-28`
- 问题: 格式化逻辑在多处重复
- 建议: 提取为 `formatCurrency()` 工具函数
```
---
## 审查自检清单(审查者使用)
### 提交审查前自检
- [ ] 是否审查了所有变更文件?
- [ ] 是否理解了变更的业务目的?
- [ ] Critical 问题是否都已标注?
- [ ] 反馈是否具有可操作性(有建议、有代码)?
- [ ] 是否检查了测试覆盖?
- [ ] 是否有正面反馈?