pr-check
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePR Check — Backend Checklist
PR检查 — 后端检查表
Overview
概述
Runs both checklists against the diff:
- Static Analysis (B1–B21) — runtime bugs, logic errors, silent failures, test correctness
- Design Review (G1–G16) — naming, complexity, documentation, test design, type precision
针对代码差异运行两组检查表:
- 静态分析(B1–B21) —— 运行时Bug、逻辑错误、静默失败、测试正确性
- 设计评审(G1–G16) —— 命名规范、复杂度、文档、测试设计、类型精度
Required Workflow
必备工作流
Step 1: Understand the diff
步骤1:理解代码差异
Run to see all changed files and understand the scope.
git diff main...HEAD执行查看所有变更文件,了解修改范围。
git diff main...HEADPart 1 — Static Analysis (B1–B21)
第一部分 — 静态分析(B1–B21)
B1. Module Initialisation Timing
B1. 模块初始化时机
- Are any declarations at module top-level (outside a class)?
const FOO = process.env.BAR - Env vars must be read via at runtime, not
ConfigServiceat import time.process.env
- 是否有这样的声明位于模块顶层(类外部)?
const FOO = process.env.BAR - 环境变量必须在运行时通过读取,而非在导入时通过
ConfigService读取。process.env
B2. Silent Logic Bugs
B2. 静默逻辑Bug
- chains between non-empty strings always resolve to the first operand — use
||array or union type.as const - Optional fields () used in comparisons like
field?: Tsilently returnfield <= 0whenfalse.undefined - Check if reordering a chain changed which value is evaluated first.
||
- 非空字符串之间的链式判断始终会解析为第一个操作数 —— 请使用
||数组或联合类型。as const - 可选字段()用于
field?: T这类比较时,当值为field <= 0会静默返回undefined。false - 检查链式判断的顺序调整是否改变了值的优先解析逻辑。
||
B3. Unused Code
B3. 未使用代码
- Unused imports — especially after refactors.
- Variables assigned an initial value that is always overwritten before first read — declare without initial value.
- 未使用的导入 —— 尤其是重构后的残留导入。
- 被赋值初始值但在首次读取前始终被覆盖的变量 —— 应声明时不赋初始值。
B4. Test Assertion No-Ops
B4. 测试断言无操作
- Search spec files for without
.toHaveBeenCalled— these silently pass always.() - Same for ,
.toBeDefined,.toBeTruthy,.toBeFalsywithout.toBeNull.()
- 在测试文件中搜索没有加的
()—— 这类代码会始终静默通过。.toHaveBeenCalled - 、
.toBeDefined、.toBeTruthy、.toBeFalsy同理,不加.toBeNull也会无意义通过。()
B5. Flaky Tests from Random Data
B5. 随机数据导致的不稳定测试
- generating a value that can randomly match the hardcoded value being tested against.
faker - Use explicit hardcoded boundary values for mismatch tests.
- 生成的值可能随机匹配测试中硬编码的预期值。
faker - 对于不匹配测试,请使用明确的硬编码边界值。
B6. Redundant Type Definitions
B6. 冗余类型定义
- New interfaces structurally identical to existing types — search before defining.
- 新定义的接口与现有类型结构完全一致 —— 定义前请先搜索确认。
B7. Wrong Test Title
B7. 错误的测试标题
- Test description says "X fails" but the mock actually simulates "Y fails".
- Test name and /
mockRejectedValuemust match.mockResolvedValue
- 测试描述写的是“X失败”,但mock实际模拟的是“Y失败”。
- 测试名称必须与/
mockRejectedValue的逻辑匹配。mockResolvedValue
B8. Missing Critical Assertions
B8. 缺失关键断言
- Happy-path tests: are all side effects asserted (DB writes, status updates, service calls)?
- Guard/skip tests: are downstream methods asserted ?
.not.toHaveBeenCalled()
- 正常流程测试:是否对所有副作用都做了断言(数据库写入、状态更新、服务调用)?
- 守卫/跳过测试:是否断言下游方法?
.not.toHaveBeenCalled()
B9. URL Path Construction
B9. URL路径构建
- Same path string used for both signature generation and the HTTP call?
- Base URL missing a required path prefix (e.g., )?
/api
- 签名生成和HTTP调用是否使用了相同的路径字符串?
- 基础URL是否缺失必填的路径前缀(例如)?
/api
B10. Status Mapper Completeness
B10. 状态映射完整性
- Does at least one external status map to each terminal internal state?
- Any value mapped to or unhandled should log + Sentry, not silently disappear.
null
- 每个终端内部状态是否至少有一个外部状态与之映射?
- 任何被映射为或未处理的状态都应记录日志并上报Sentry,而非静默消失。
null
B11. API Response Envelope Type Accuracy
B11. API响应信封类型准确性
- TypeScript type for each API response must match the actual envelope key from the API docs.
- Don't assume — verify the actual key name for every endpoint.
{ data: [] }
- 每个API响应的TypeScript类型必须与API文档中的实际信封键名匹配。
- 不要默认假设是—— 请验证每个端点的实际键名。
{ data: [] }
B12. Webhook Controller Must Not Swallow All Exceptions
B12. Webhook控制器不得吞噬所有异常
- Blanket try-catch returning means failed webhooks are silently dropped.
200 OK - Unexpected exceptions should propagate or return 4xx/5xx.
- 全局try-catch返回会导致失败的Webhook被静默丢弃。
200 OK - 意外异常应向上传播或返回4xx/5xx状态码。
B13. No-Op Stubs Must Have No Side Effects
B13. 无操作存根不得有副作用
- A deferred/stub method must not still call , fire notifications, or write DB records.
updateLastSync - A true no-op: only a comment or .
return
- 延迟/存根方法不得仍调用、发送通知或写入数据库记录。
updateLastSync - 真正的无操作:仅保留注释或语句。
return
B14. Duplicate Error Handling in Strategy and Caller
B14. 策略层与调用方重复处理错误
- If a method catches, logs, Sentries, and rethrows — the caller must NOT catch and log the same error again.
- 如果一个方法已经捕获、记录日志、上报Sentry并重新抛出异常 —— 调用方不得再次捕获并记录相同的错误。
B15. Enum Constants in Comparisons
B15. 比较时使用枚举常量
- When a TypeScript enum exists for event types or status strings, use it — don't compare against string literals.
- 当存在针对事件类型或状态字符串的TypeScript枚举时,请使用枚举 —— 不要直接与字符串字面量比较。
B16. Dead Type Checks in Catch Blocks
B16. Catch块中的无效类型检查
- in a DB/repository catch block will never be true — it's dead code.
instanceof AxiosError
- 在数据库/仓库层的catch块中使用永远不会为true —— 这是无效代码。
instanceof AxiosError
B17. Database Transactions for Multi-Step Writes
B17. 多步骤写入需使用数据库事务
- Multiple sequential saves (2+) across entities must be wrapped in a TypeORM transaction.
- 跨实体的多次连续保存(2次及以上)必须包裹在TypeORM事务中。
B18. Race Condition / TOCTOU on State Guards
B18. 状态守卫的竞态条件/TOCTOU问题
- Check-then-act is not atomic — fix with atomic conditional update or .
SELECT FOR UPDATE
- 先检查后执行的操作不是原子性的 —— 请使用原子条件更新或修复。
SELECT FOR UPDATE
B19. Missing Null Check on findOne()
findOne()B19. findOne()
结果缺失空值检查
findOne()- returns
findOne()when no record matches — guard before use.null - Pattern:
if (!record) throw new NotFoundException('record not found');
- 在无匹配记录时会返回
findOne()—— 使用前必须做守卫检查。null - 推荐模式:
if (!record) throw new NotFoundException('record not found');
B20. Credentials in Documentation Files
B20. 文档文件中包含凭据
- Real API tokens in files are committed to git history permanently.
.md - Use clearly fake placeholders: ,
YOUR_BEARER_TOKEN_HERE.<api-token>
- 文件中的真实API令牌会被永久提交到git历史中。
.md - 请使用明确的假占位符:、
YOUR_BEARER_TOKEN_HERE。<api-token>
B21. Input Normalization Consistency
B21. 输入规范化一致性
- New flows must normalize user-provided strings (email, username) the same way as existing flows.
- 新流程必须与现有流程以相同方式规范化用户提供的字符串(邮箱、用户名)。
Part 2 — Design Review (G1–G16)
第二部分 — 设计评审(G1–G16)
G1. Method Complexity
G1. 方法复杂度
- Methods longer than ~40 lines with multiple concerns — extract to focused helpers.
- 超过约40行且包含多个关注点的方法 —— 应拆分为专注的辅助方法。
G2. Named Constants
G2. 命名常量
- Magic strings for status/state values — check if constants already exist.
- 状态/状态值使用魔法字符串 —— 请检查是否已有对应的常量定义。
G3. Naming Specificity
G3. 命名特异性
- Vague or mismatched names — method named for X that contains logic for Y.
- 模糊或不匹配的命名 —— 方法名标注为X,但实际包含Y的逻辑。
G4. Documentation
G4. 文档
- New method similar to an existing one — explain the difference in a comment.
- New state transitions — update state machine or architecture docs.
- 与现有方法类似的新方法 —— 请在注释中说明差异。
- 新的状态转换 —— 请更新状态机或架构文档。
G5. Test Data Realism
G5. 测试数据真实性
- Numeric fields (price, quantity, dimensions) using instead of number generators.
faker.string.alphanumeric() - Currency fields using random strings instead of .
faker.finance.currencyCode()
- 数值字段(价格、数量、尺寸)使用而非数值生成器。
faker.string.alphanumeric() - 货币字段使用随机字符串而非。
faker.finance.currencyCode()
G6. Test Naming Clarity
G6. 测试命名清晰度
- Vague words: "completely", "properly", "correctly" without specifics.
- Preferred: .
should <do something> when <condition>
- 使用模糊词汇:“completely”、“properly”、“correctly”但未说明具体内容。
- 推荐格式:。
should <执行操作> when <条件>
G7. No Real URLs or Credentials in Tests or Service Defaults
G7. 测试或服务默认值中不得包含真实URL或凭据
- All test URLs must be clearly dummy (,
https://example.com).https://test.invalid - No production-looking base URL hardcoded as a service default.
- 所有测试URL必须是明确的虚拟地址(、
https://example.com)。https://test.invalid - 服务默认值中不得硬编码类似生产环境的基础URL。
G8. Test Assertion Completeness
G8. 测试断言完整性
- Crypto/signature operations: validate the output value, not just that the function ran.
- Retry/token-refresh flows: add a test for when the retry itself also fails.
- 加密/签名操作:请验证输出值,而非仅验证函数是否执行。
- 重试/令牌刷新流程:请添加重试本身也失败的测试场景。
G9. .env.example
Completeness
.env.exampleG9. .env.example
完整性
.env.example- All new env vars added to with a descriptive placeholder.
.env.example
- 所有新增的环境变量都应添加到中,并附上描述性占位符。
.env.example
G10. Type Precision
G10. 类型精度
- chains that should be
||arrays or union literal types.as const - Optional fields that should be required.
- 应改为数组或联合字面量类型的
as const链式判断。|| - 应设为必填的可选字段。
G11. Logical Grouping
G11. 逻辑分组
- New types/constants placed in the section matching their domain.
- 新类型/常量应放置在与其领域匹配的代码段中。
G12. Idempotency of External Calls in Retry Contexts
G12. 重试场景中外调用用的幂等性
- In cron/retry-driven pushes: if the call succeeds externally but fails to record locally, the next retry creates a duplicate.
- 在定时任务/重试驱动的推送中:如果外部调用成功但本地记录失败,下一次重试会创建重复数据。
G13. Log Level for Security Failures
G13. 安全失败的日志级别
- Failed HMAC validation, invalid auth → , not
error.warn
- HMAC验证失败、无效认证 → 应使用级别日志,而非
error。warn
G14. Use Existing Helpers Consistently
G14. 一致使用现有辅助工具
- Before constructing a cache key or identifier inline, check if a helper already exists.
- 在手动构造缓存键或标识符前,请检查是否已有对应的辅助工具。
G15. YAGNI — Avoid Implementing Unbuilt Features
G15. YAGNI原则 —— 避免实现未规划的功能
- Constants or fields for features not yet built should be deferred.
- 针对尚未开发的功能的常量或字段应延迟添加。
G16. Database Schema & Migration Quality
G16. 数据库 schema 与迁移质量
- Entity column decorators must match the actual DB type (,
timestamptz,bigint).smallint - Numeric columns for whole numbers: use /
int/bigint, notsmallint/float.decimal - Migration columns: specify explicit length where the domain has a known maximum.
varchar - Column defaults must match nullable design.
- Index columns must match actual query patterns — UNIQUE index on lookup columns.
- 实体列装饰器必须与实际数据库类型匹配(、
timestamptz、bigint)。smallint - 用于整数的数值列:请使用/
int/bigint,而非smallint/float。decimal - 迁移中的列:当领域有已知最大值时,请指定明确长度。
varchar - 列默认值必须与可空设计匹配。
- 索引列必须与实际查询模式匹配 —— 查找列应添加UNIQUE索引。
Step 2: Report Findings
步骤2:报告发现的问题
Group by part. For each issue:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.
[G3] path/to/file.ts:42
Issue: method name is too vague — doesn't communicate what it does without reading the body.
Fix: rename to something that describes the actual behaviour.按部分分组。每个问题格式如下:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.
[G3] path/to/file.ts:42
Issue: method name is too vague — doesn't communicate what it does without reading the body.
Fix: rename to something that describes the actual behaviour.Step 3: Final Verdict
步骤3:最终结论
Static analysis: X high, Y medium, Z low
Design review: X blocking, Y nit
Overall: READY / NEEDS CHANGESStatic analysis: X high, Y medium, Z low
Design review: X blocking, Y nit
Overall: READY / NEEDS CHANGESIndividual Skills
独立技能项
Run separately for a focused check:
- — B1–B21 static analysis only
pr-check-static - — G1–G16 design patterns only
pr-check-style - — F1–F16 React/Vue/Next.js/Tailwind
pr-check-frontend
可单独运行以进行针对性检查:
- —— 仅运行B1–B21静态分析项
pr-check-static - —— 仅运行G1–G16设计模式项
pr-check-style - —— 运行F1–F16 React/Vue/Next.js/Tailwind相关检查
pr-check-frontend