Files
BOJ/.claude/skills/code-review-skill/reference/code-quality-universal.md

489 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Universal Code Quality Anti-Patterns
> 语言无关的代码质量反模式指南,覆盖代码复用、抽象泄漏、参数膨胀、嵌套条件、字符串类型化、TOCTOU、空操作更新等核心主题。适用于所有语言的 PR 审查。
## 目录
- [代码复用审查](#代码复用审查)
- [参数膨胀](#参数膨胀)
- [抽象泄漏](#抽象泄漏)
- [字符串类型化](#字符串类型化)
- [嵌套条件表达式](#嵌套条件表达式)
- [复制粘贴变种](#复制粘贴变种)
- [空操作更新](#空操作更新)
- [TOCTOU 竞争条件](#toctou-竞争条件)
- [过度宽泛操作](#过度宽泛操作)
- [冗余状态](#冗余状态)
- [通用质量审查清单](#通用质量审查清单)
---
## 代码复用审查
Before accepting new code, search the existing codebase for reusable utilities.
### 搜索现有工具函数
```python
# ❌ 新写的路径拼接逻辑——项目中已有 PathBuilder
def get_config_path(name):
base = os.environ.get("APP_ROOT", ".")
return os.path.join(base, "config", name + ".json")
# ✅ 使用已有的 PathBuilder
def get_config_path(name):
return PathBuilder.config(f"{name}.json")
```
```javascript
// ❌ 手写 debounce——项目已有 lodash 或 utils/debounce.ts
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
// ✅ 使用已有的工具函数
import { debounce } from "@/utils/debounce";
```
**审查要点:**
- 新增函数是否与已有 utility 重名或功能重叠?
- inline 逻辑是否可以提取为已有模块的调用?
- 检查相邻文件和 shared/utils 目录
---
## 参数膨胀
### 函数参数不断增长
```python
# ❌ 每次新需求加一个参数
def create_user(name, email, role, team, active, avatar_url, timezone):
...
# ✅ 使用配置对象 / dataclass
@dataclass
class CreateUserParams:
name: str
email: str
role: Role = Role.MEMBER
team: str | None = None
active: bool = True
avatar_url: str | None = None
timezone: str = "UTC"
def create_user(params: CreateUserParams) -> User:
...
```
```typescript
// ❌ 6+ 个 positional 参数
function renderWidget(
title: string, width: number, height: number,
theme: string, collapsible: boolean, icon: string
) { ... }
// ✅ Options object pattern
interface WidgetOptions {
title: string;
width?: number;
height?: number;
theme?: "light" | "dark";
collapsible?: boolean;
icon?: string;
}
function renderWidget(options: WidgetOptions) { ... }
```
**审查要点:**
- 函数参数是否 ≥ 4 个?考虑 options object / dataclass
- 新参数是否只是布尔标志?考虑 enum 或 strategy pattern
- 是否有 `enable_x`, `disable_y` 这类互斥参数?
---
## 抽象泄漏
### 暴露内部实现细节
```python
# ❌ 返回内部 ORM 对象——调用者被迫了解 SQLAlchemy
def get_users():
return session.query(User).filter(User.active == True).all()
# ✅ 返回 domain 对象,隐藏持久化层
def get_active_users() -> list[UserDTO]:
rows = user_repo.find_active()
return [UserDTO.from_row(r) for r in rows]
```
```typescript
// ❌ 组件接收 API response 原始结构
<UserCard user={apiResponse.data.results[0]} />
// ✅ 组件接收 domain 类型,adapter 处理映射
interface UserSummary {
displayName: string;
avatarUrl: string;
}
<UserCard user={adaptUser(apiResponse)} />
```
**审查要点:**
- 函数返回类型是否泄露底层实现(ORM, HTTP client, file format)?
- 组件/函数是否依赖外部系统的数据结构?
- 是否破坏了已有的抽象边界?
---
## 字符串类型化
### 用原始字符串代替常量/枚举
```python
# ❌ Magic strings 散落各处
if status == "active":
...
if role == "admin":
...
# ✅ 使用 enum
class Status(StrEnum):
ACTIVE = "active"
SUSPENDED = "suspended"
ARCHIVED = "archived"
if user.status == Status.ACTIVE:
...
```
```typescript
// ❌ Raw string event names——拼写错误不会报错
emitter.emit("userCreated", data);
emitter.on("usercreated", handler); // bug: typo
// ✅ 常量或 branded type
const Events = {
USER_CREATED: "userCreated",
USER_SUSPENDED: "userSuspended",
} as const;
emitter.emit(Events.USER_CREATED, data);
```
**审查要点:**
- 是否用字符串代替了已有的 enum/union type
- 事件名、action type、status 值是否散落在多个文件?
- 字符串比较是否 case-sensitive 但未验证?
---
## 嵌套条件表达式
### 三元链和嵌套 if/else
```python
# ❌ 三元链难以阅读
label = (
"Admin" if role == "admin" else
"Manager" if role == "manager" else
"Viewer" if role == "viewer" else
"Unknown"
)
# ✅ 查找表或 match
ROLE_LABELS = {
"admin": "Admin",
"manager": "Manager",
"viewer": "Viewer",
}
label = ROLE_LABELS.get(role, "Unknown")
```
```typescript
// ❌ 嵌套三元
const bg = isHovered
? isSelected ? "blue" : "gray"
: isSelected ? "navy" : "white";
// ✅ 查找表(lookup map
const bgMap: Record<string, string> = {
"true-true": "blue",
"true-false": "gray",
"false-true": "navy",
"false-false": "white",
};
const bg = bgMap[`${isHovered}-${isSelected}`];
```
```python
# ❌ 嵌套 if 3+ 层
def process(order):
if order is not None:
if order.items:
for item in order.items:
if item.price > 0:
...
# ✅ Early return + guard clauses
def process(order):
if not order or not order.items:
return
for item in order.items:
if item.price <= 0:
continue
...
```
**审查要点:**
- 三元表达式是否嵌套 ≥ 2 层?
- if/else 嵌套是否 ≥ 3 层?
- 能否用 lookup table、early return 或 match 替换?
---
## 复制粘贴变种
### 近乎重复的代码块
```python
# ❌ 两个函数几乎一样,只有字段名不同
def format_user(user):
return f"{user.first_name} {user.last_name} ({user.email})"
def format_employee(emp):
return f"{emp.first_name} {emp.last_name} ({emp.work_email})"
# ✅ 统一抽象
def format_person(first: str, last: str, email: str) -> str:
return f"{first} {last} ({email})"
```
```typescript
// ❌ Copy-paste handler 只改了 URL
async function deletePost(id: string) {
await fetch(`/api/posts/${id}`, { method: "DELETE" });
router.push("/posts");
}
async function deleteComment(id: string) {
await fetch(`/api/comments/${id}`, { method: "DELETE" });
router.push("/comments");
}
// ✅ 参数化
async function deleteResource(resource: string, id: string) {
await fetch(`/api/${resource}/${id}`, { method: "DELETE" });
router.push(`/${resource}`);
}
```
**审查要点:**
- 是否有 ≥ 2 段代码仅变量名/URL/字符串不同?
- 能否提取参数化的共享函数?
- 是否可以用 template method 或 strategy 消除变种?
---
## 空操作更新
### 无条件触发状态更新
```typescript
// ❌ 每次 poll 都触发 update——即使数据未变
useEffect(() => {
const interval = setInterval(() => {
fetch("/api/status").then(r => r.json()).then(setStatus);
}, 5000);
return () => clearInterval(interval);
}, []);
// ✅ 仅在值变化时更新
useEffect(() => {
const interval = setInterval(() => {
fetch("/api/status")
.then(r => r.json())
.then(data => {
setStatus(prev => isEqual(prev, data) ? prev : data);
});
}, 5000);
return () => clearInterval(interval);
}, []);
```
```python
# ❌ 每次 loop 都写 DB——即使值未变
for item in items:
item.status = compute_status(item)
session.commit()
# ✅ 仅在变化时写入
for item in items:
new_status = compute_status(item)
if item.status != new_status:
item.status = new_status
session.commit()
```
**审查要点:**
- polling / interval / event handler 是否无条件更新?
- wrapper function 是否尊重 same-reference return
- DB 写入是否检查了实际变化?
---
## TOCTOU 竞争条件
### Time-of-Check-to-Time-of-Use
```python
# ❌ 先检查后操作——中间文件可能被删除/创建
if os.path.exists(path):
with open(path) as f:
data = f.read()
# ✅ 直接操作 + 处理异常
try:
with open(path) as f:
data = f.read()
except FileNotFoundError:
data = None
```
```python
# ❌ 检查余额 → 扣款 两步操作不是原子的
if account.balance >= amount:
account.balance -= amount
# ✅ 原子操作或锁
with account.lock:
if account.balance < amount:
raise InsufficientFundsError()
account.balance -= amount
```
```typescript
// ❌ Check-then-act 在 async 环境中不安全
if (!fileExists(path)) {
await writeFile(path, content);
}
// ✅ 直接操作 + catch
try {
await writeFile(path, content, { flag: "wx" });
} catch (e) {
if (e.code === "EEXIST") { /* handle */ }
else throw e;
}
```
**审查要点:**
- `if exists → operate` 模式是否可替换为 `try operate → catch`
- 多步状态变更是否在事务/锁内?
- async 操作中 check 和 act 之间是否有 await
---
## 过度宽泛操作
### 读取过多数据
```python
# ❌ 读取整个文件再取第一行
content = Path("log.txt").read_text()
first_line = content.split("\n")[0]
# ✅ 只读第一行,不加载整个文件
with open("log.txt") as f:
first_line = f.readline()
```
```typescript
// ❌ 加载所有 items 再过滤
const allItems = await db.query("SELECT * FROM orders");
const pending = allItems.filter(o => o.status === "pending");
// ✅ 数据库层过滤
const pending = await db.query(
"SELECT * FROM orders WHERE status = ?", ["pending"]
);
```
```python
# ❌ 读取整个列表找一条记录
users = list(User.objects.all())
user = next(u for u in users if u.id == user_id)
# ✅ 精确查询
user = User.objects.get(id=user_id)
```
**审查要点:**
- 是否读取了整个集合/文件再只用一小部分?
- 能否将过滤推到数据库/存储层?
- API 调用是否支持 pagination/limit 参数?
---
## 冗余状态
### 状态可以被推导
```typescript
// ❌ 同时存储 fullName 和 firstName + lastName
interface User {
firstName: string;
lastName: string;
fullName: string; // redundant
}
// ✅ fullName 是推导值
interface User {
firstName: string;
lastName: string;
}
const fullName = `${user.firstName} ${user.lastName}`;
```
```python
# ❌ 缓存值在源数据变化时可能过时
class Order:
total: float
item_count: int # redundant if len(items) gives the same
items: list[Item]
# ✅ 推导或 property
class Order:
items: list[Item]
@property
def total(self) -> float:
return sum(item.price for item in self.items)
@property
def item_count(self) -> int:
return len(self.items)
```
**审查要点:**
- 是否有字段可以从其他字段推导?
- 缓存值是否有 invalidation 机制?
- observer/effect 是否可以替换为直接调用?
---
## 通用质量审查清单
- [ ] **复用审查**: 搜索了现有 utility/helper,没有重复造轮子?
- [ ] **参数数量**: 函数参数 ≤ 3 个?超过则用 options object / dataclass
- [ ] **抽象边界**: 返回类型没有暴露内部实现细节(ORM、HTTP client、file format)?
- [ ] **类型安全**: 没有 magic strings 代替已有的 enum/constant/union type
- [ ] **条件深度**: 三元嵌套 ≤ 1 层?if/else 嵌套 ≤ 2 层?
- [ ] **DRY**: 没有 copy-paste-with-variation(≥ 2 段近似代码)?
- [ ] **空操作防护**: polling / interval / event handler 有 change-detection guard
- [ ] **TOCTOU**: `if exists → operate` 替换为 `try operate → catch`
- [ ] **数据精度**: 没有读取整个集合/文件只为了取子集?
- [ ] **冗余状态**: 没有可以从其他字段推导的存储字段?