关于 debugging
debugging 是一项面向 GoodVibes 的系统化调试技能,供开发者调查运行时错误、异常行为和构建问题。它按复现、隔离、定位根因、修复和验证组织流程,也涵盖网络与状态调试。
主要功能
- 01按阶段开展系统化调试
- 02分析错误信息与调用栈
- 03追踪数据流和边界条件
- 04检查依赖、导入与类型问题
- 05修复后运行类型检查和测试
- 06调查网络请求及状态问题
适用场景
- 01定位 TypeScript 类型错误
- 02排查运行时异常或逻辑错误
- 03分析 API 请求与响应故障
- 04查找构建错误或性能瓶颈
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mgd34msu/ goodvibes-plugin --skill debugging安装后在 Claude Code 中输入/debugging确认已加载。
附带文件references/debugging-patterns.mdscripts/validate-debugging.sh
详细介绍
资源
scripts/
validate-debugging.sh
references/
debugging-patterns.md
调试质量技能
此技能将教你如何使用 GoodVibes 精准工具进行系统化调试。有效的调试需要采用有条理的方法:重现问题、隔离原因、找出根本问题、应用修复并验证解决方案。
何时使用此技能
在以下情况下加载此技能:
- 调查运行时错误或异常
- 追踪意外行为或逻辑错误
- 分析网络请求/响应问题
- 调试状态管理问题
- 诊断性能瓶颈
- 调查构建或编译错误
- 理解 TypeScript 类型错误
- 执行根因分析
触发短语:“调试这个错误”“为什么这会失败”“调查这个问题”“追踪问题”“分析错误”。
核心工作流
阶段 1:重现——确认问题
开始调试前,请确认你能够稳定地重现问题。
步骤 1.1:收集错误上下文
收集所有可用的错误信息。
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1"
- cmd: "npm run lint 2>&1"
- cmd: "npm run build 2>&1"
verbosity: standard
需要捕获的信息:
- 完整的错误消息和堆栈跟踪
- 错误类型和代码(如适用)
- 文件路径和行号
- 时间戳和环境信息
- 触发错误的用户操作
步骤 1.2:查找错误出现位置
在代码库中搜索类似错误。
discover:
queries:
- id: error_throw_sites
type: grep
pattern: "throw new (Error|TypeError|RangeError)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: error_handlers
type: grep
pattern: "catch\\s*\\("
glob: "**/*.{ts,tsx,js,jsx}"
- id: console_errors
type: grep
pattern: "console\\.(error|warn)"
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: locations
错误模式类别:
| 错误模式 | 含义 | 常见原因 |
|---|---|---|
| ReferenceError | 变量未定义 | 拼写错误、缺少导入、作用域问题 |
| TypeError | 使用了错误类型 | null/undefined、错误的方法、类型不匹配 |
| RangeError | 值超出范围 | 数组索引、数值溢出 |
| SyntaxError | 语法无效 | 解析错误、格式错误的 JSON |
| NetworkError | 请求失败 | CORS、超时、404、身份验证失败 |
步骤 1.3:在本地重现
运行失败的操作,确认问题可以重现。
precision_exec:
commands:
- cmd: "npm run dev"
timeout_ms: 5000
verbosity: minimal
重现检查清单:
- 错误能够稳定重现
- 已确定重现问题的最少步骤
- 环境与生产环境一致(Node 版本、依赖项)
- 错误消息与报告的问题一致
阶段 2:隔离——缩小原因范围
缩小问题范围,找出错误的来源。
步骤 2.1:阅读堆栈跟踪
分析调用堆栈,了解执行流程。
堆栈跟踪示例:
TypeError: Cannot read property 'name' of undefined
at getUserName (src/utils/user.ts:42:18)
at UserProfile (src/components/UserProfile.tsx:15:22)
at renderWithHooks (node_modules/react/cjs/react.development.js:1234)
阅读策略:
- 从顶部开始 - 实际错误位置
- 反向追踪 - 沿调用链查找
- 忽略 node_modules - 专注于你的代码
- 找到入口点 - 错误数据来自哪里?
precision_read:
files:
- path: "src/utils/user.ts"
extract: content
range: {start: 35, end: 50}
- path: "src/components/UserProfile.tsx"
extract: content
range: {start: 10, end: 25}
verbosity: standard
步骤 2.2:追踪数据流
从数据源开始,追踪数据直至错误位置。
discover:
queries:
- id: function_calls
type: grep
pattern: "getUserName\\("
glob: "**/*.{ts,tsx}"
- id: data_sources
type: grep
pattern: "(fetch|axios|prisma).*user"
glob: "**/*.{ts,tsx}"
- id: prop_passing
type: grep
pattern: "user=\\{"
glob: "**/*.tsx"
verbosity: locations
数据流分析:
- 数据源自哪里?(API、数据库、props)
- 发生了哪些转换?(map、filter、解构)
- 做出了哪些假设?(非 null、特定结构)
- 数据可能在哪里变成 undefined?
步骤 2.3:检查边界条件
测试容易出错的边缘情况。
discover:
queries:
- id: array_access
type: grep
pattern: "\\[[0-9]+\\]|\\[.*\\]"
glob: "**/*.{ts,tsx}"
- id: null_checks
type: grep
pattern: "(if.*===.*null|if.*===.*undefined|\\?\\.|\\?\\?)"
glob: "**/*.{ts,tsx}"
- id: optional_chaining
type: grep
pattern: "\\?\\."
glob: "**/*.{ts,tsx}"
verbosity: locations
常见边界问题:
| 问题 | 示例 | 修复方法 |
|---|---|---|
| 数组越界 | arr[arr.length] | 检查 arr.length > 0 |
| 访问 null/undefined | user.name | 使用 user?.name |
| 除以零 | total / count | 检查 count !== 0 |
| 空字符串操作 | str[0] | 检查 str.length > 0 |
| 对象缺少键 | obj.key | 使用 obj?.key ?? default |
阶段 3:识别——找出根本原因
确定根本问题,而不只是表面症状。
步骤 3.1:分析错误模式
对错误进行分类,了解常见原因。
类型错误
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1 | head -50"
verbosity: standard
常见 TypeScript 错误:
| 错误代码 | 含义 | 修复方法 |
|---|---|---|
| TS2339 | 属性不存在 | 将属性添加到类型中,或使用可选链 |
| TS2345 | 参数类型不匹配 | 修正类型或添加类型断言 |
| TS2322 | 类型不可赋值 | 更改类型或修正值 |
| TS2571 | 对象的类型为 unknown | 添加类型守卫或断言 |
| TS7006 | 参数隐式具有 any 类型 | 添加参数类型注解 |
运行时错误
discover:
queries:
- id: unsafe_access
type: grep
pattern: "\\.[a-zA-Z]+(?!\\?)"
glob: "**/*.{ts,tsx}"
- id: async_errors
type: grep
pattern: "await.*(?!try)"
glob: "**/*.{ts,tsx}"
verbosity: files_only
常见运行时错误:
- 无法读取 undefined 的属性 X
- 无法读取 null 的属性 X
- X 不是函数
- X 不可迭代
- 超出最大调用堆栈大小(无限递归)
步骤 3.2:检查依赖项和导入
模块解析错误很常见。
discover:
queries:
- id: imports
type: grep
pattern: "^import.*from"
glob: "**/*.{ts,tsx}"
- id: dynamic_imports
type: grep
pattern: "import\\("
glob: "**/*.{ts,tsx}"
- id: require_statements
type: grep
pattern: "require\\("
glob: "**/*.{ts,js}"
verbosity: locations
常见导入问题:
| 问题 | 表现 | 修复方法 |
|---|---|---|
| 缺少导出 | 模块没有导出的成员 | 在源文件中添加导出 |
| 循环依赖 | 初始化前无法访问 | 重新调整导入结构 |
| 路径错误 | 找不到模块 | 修正相对路径或别名 |
| 默认导入与命名导入不匹配 | X 不是函数 | 使用 import X 或 import { X } |
| 仅类型导入 | 无法将其用作值 | 移除 type 关键字 |
步骤 3.3:验证假设
质疑关于数据结构和状态的假设。
precision_grep:
queries:
- id: type_assertions
pattern: "as (unknown|any|[A-Z][a-zA-Z]+)"
glob: "**/*.{ts,tsx}"
output:
format: context
context_before: 2
context_after: 2
verbosity: standard
危险的假设:
- API 总是返回预期的数据结构
- 数组永远不会为空
- 用户始终已通过身份验证
- 数据始终有效
- 始终会提供 Props
通过以下方式验证:
- 运行时类型验证(Zod、Yup)
- 类型守卫(
if (typeof x === 'string')) - 空值检查(
if (x != null)) - 默认值(
x ?? defaultValue)
阶段 4:修复——应用解决方案
根据根本原因分析实施修复。
步骤 4.1:选择正确的修复方法
根据问题类型选择相应的修复方法。
类型安全修复
// Before: Implicit any
function getUser(id) {
return users.find(u => u.id === id);
}
// After: Explicit types
function getUser(id: string): User | undefined {
return users.find(u => u.id === id);
}
空值安全修复
// Before: Unsafe access
const name = user.profile.name;
// After: Optional chaining
const name = user?.profile?.name ?? 'Anonymous';
异步错误处理
// Before: Unhandled promise
await fetchData();
// After: Try/catch
try {
await fetchData();
} catch (error: unknown) {
logger.error('Failed to fetch data', { error });
throw new AppError('Data fetch failed', { cause: error });
}
数组边界修复
// Before: Unsafe access
const first = items[0];
// After: Safe access
const first = items.length > 0 ? items[0] : null;
// Or: const first = items.at(0) ?? null;
步骤 4.2:添加防御性代码
防止未来出现类似错误。
输入验证:
import { z } from 'zod';
const userSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1).max(100),
});
function processUser(data: unknown) {
const user = userSchema.parse(data); // Throws if invalid
// Now user is type-safe
}
类型守卫:
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value
);
}
if (isUser(data)) {
// TypeScript knows data is User
logger.info('User email', { email: data.email });
}
错误边界(React):
import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error }: { error: Error }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
</div>
);
}
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MyComponent />
</ErrorBoundary>
步骤 4.3:应用修复
使用 precision_edit 进行修改。
precision_edit:
edits:
- path: "src/utils/user.ts"
find: |
function getUserName(user) {
return user.name;
}
replace: |
function getUserName(user: User | undefined): string {
return user?.name ?? 'Unknown';
}
verbosity: minimal
阶段 5:验证——确认修复有效
确认修复解决了问题,且没有造成其他问题。
步骤 5.1:类型检查
确保 TypeScript 错误已解决。
precision_exec:
commands:
- cmd: "npm run typecheck"
verbosity: standard
所有类型错误都必须解决。
步骤 5.2:运行测试
确认测试通过。
precision_exec:
commands:
- cmd: "npm test"
verbosity: standard
如果测试失败:
- 修复引入了回归问题
- 需要更新测试预期
- 测试发现了另一个问题
步骤 5.3:手动验证
重现原始错误场景。
precision_exec:
commands:
- cmd: "npm run dev"
timeout_ms: 5000
verbosity: minimal
验证清单:
- 原始错误不再出现
- 已处理边缘情况(null、空值、大值)
- 未引入新错误
- 性能未下降
- 用户体验有所改善
阶段 6:网络调试
调试 API 请求和响应。
步骤 6.1:查找网络调用
定位所有 HTTP 请求。
discover:
queries:
- id: fetch_calls
type: grep
pattern: "fetch\\("
glob: "**/*.{ts,tsx,js,jsx}"
- id: axios_calls
type: grep
pattern: "axios\\.(get|post|put|delete|patch)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: api_routes
type: glob
patterns: ["src/app/api/**/*.ts", "pages/api/**/*.ts"]
verbosity: locations
步骤 6.2:检查请求/响应处理
验证网络调用的错误处理。
precision_grep:
queries:
- id: fetch_with_catch
pattern: "fetch\\([^)]+\\).*catch"
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: context
context_after: 5
verbosity: standard
常见网络问题:
| 问题 | 表现 | 修复方法 |
|---|---|---|
| CORS 错误 | 被 CORS 策略阻止 | 为 API 添加 CORS 标头 |
| 401 未授权 | 缺少或令牌无效 | 添加身份验证标头,刷新令牌 |
| 404 未找到 | URL 或路由错误 | 修正端点路径 |
| 500 服务器错误 | 后端异常 | 检查服务器日志,修复后端 |
| 超时 | 请求耗时过长 | 延长超时时间,优化后端 |
| 网络故障 | 获取失败 | 检查网络连接,添加重试逻辑 |
调试 CORS:
// Next.js API route
export async function GET(request: Request) {
return Response.json(data, {
headers: {
'Access-Control-Allow-Origin': '*', // Development only - use specific origin in production
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
调试身份验证:
// Check for auth header
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const session = await verifyToken(token);
} catch (error: unknown) {
logger.error('Token verification failed', { error });
return Response.json({ error: 'Invalid token' }, { status: 401 });
}
步骤 6.3:验证请求载荷
确保请求数据正确。
discover:
queries:
- id: request_bodies
type: grep
pattern: "(body:|JSON.stringify)"
glob: "**/*.{ts,tsx}"
- id: validation_schemas
type: grep
pattern: "z\\.object\\("
glob: "**/*.{ts,tsx}"
verbosity: locations
请求验证:
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export async function POST(request: Request) {
const body = await request.json();
const result = createUserSchema.safeParse(body);
if (!result.success) {
return Response.json(
{ error: 'Validation failed', details: result.error.flatten() },
{ status: 400 }
);
}
// Now result.data is type-safe
}
阶段 7:状态调试
调试 React 状态和状态管理。
步骤 7.1:查找状态用法
定位状态声明和更新。
discover:
queries:
- id: useState_calls
type: grep
pattern: "useState<?"
glob: "**/*.{tsx,jsx}"
- id: state_updates
type: grep
pattern: "set[A-Z][a-zA-Z]*\\("
glob: "**/*.{tsx,jsx}"
- id: useEffect_hooks
type: grep
pattern: "useEffect\\("
glob: "**/*.{tsx,jsx}"
verbosity: locations
常见状态问题:
| 问题 | 症状 | 修复方法 |
|---|---|---|
| 过时闭包 | 状态值已过时 | 使用函数式更新 |
| 缺少依赖项 | useEffect 不会重新运行 | 添加到依赖项数组 |
| 无限循环 | 组件不断重新渲染 | 修复依赖项或条件 |
| 状态竞争条件 | 更新互相覆盖 | 使用 reducer 或队列 |
| 卸载时状态丢失 | 数据消失 | 提升状态或使用全局状态 |
修复过时闭包:
// Before: Stale closure
const [count, setCount] = useState(0);
function increment() {
setTimeout(() => {
setCount(count + 1); // count is stale
}, 1000);
}
// After: Functional update
function increment() {
setTimeout(() => {
setCount(prev => prev + 1); // Always current
}, 1000);
}
修复 useEffect 依赖项:
// Before: Missing dependency
useEffect(() => {
fetchData(userId);
}, []); // userId changes ignored
// After: Complete dependencies
useEffect(() => {
fetchData(userId);
}, [userId]); // Re-runs when userId changes
步骤 7.2:检查状态变更
查找直接变更状态的代码(反模式)。
discover:
queries:
- id: array_mutations
type: grep
pattern: "\\.(push|pop|shift|unshift|splice)\\("
glob: "**/*.{tsx,jsx}"
- id: object_mutations
type: grep
pattern: "[a-zA-Z]+\\.[a-zA-Z]+\\s*=\\s*"
glob: "**/*.{tsx,jsx}"
verbosity: locations
不可变更新:
// Before: Mutation
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // BAD: Mutates state
setItems(items);
// After: Immutable
setItems([...items, 4]); // GOOD: New array
// Object updates
const [user, setUser] = useState({ name: 'Alice', age: 30 });
setUser({ ...user, age: 31 }); // GOOD: New object
阶段 8:性能调试
诊断性能瓶颈。
步骤 8.1:查找性能反模式
搜索常见的性能问题。
discover:
queries:
- id: n_plus_one
type: grep
pattern: "(for|forEach|map).*await.*(prisma|db|query)"
glob: "**/*.{ts,tsx}"
- id: inline_objects
type: grep
pattern: "(onClick|onChange|style)=\\{\\{"
glob: "**/*.{tsx,jsx}"
- id: missing_memo
type: grep
pattern: "(map|filter|reduce|sort)\\("
glob: "**/*.{tsx,jsx}"
verbosity: locations
性能问题:
| 反模式 | 影响 | 修复方法 |
|---|---|---|
| N+1 查询 | 数据库查询缓慢 | 使用 include 或批量加载 |
| JSX 中的内联对象 | 不必要的重新渲染 | 提取为常量或使用 useMemo |
| 大型列表未使用虚拟化 | 渲染缓慢 | 使用 react-window 或类似工具 |
| 缺少索引 | 查询缓慢 | 添加数据库索引 |
| 不必要的重新渲染 | UI 卡顿 | 使用 React.memo、useMemo、useCallback |
步骤 8.2:分析构建性能
检查构建是否缓慢。
precision_exec:
commands:
- cmd: "npm run build"
verbosity: standard
构建性能问题:
- 包体积过大(使用包分析器检查)
- TypeScript 编译缓慢(检查 tsconfig)
- 缺少 tree-shaking(检查导入)
- 在生产环境中使用了开发依赖(检查 package.json)
阶段 9:根因分析
不止于表面症状,找出潜在问题。
步骤 9.1:连续追问五个为什么
逐步深入,找出根本原因。
示例:
- 为什么应用崩溃了? - 用户数据未定义
- 为什么用户数据未定义? - API 返回了 null
- 为什么 API 返回了 null? - 数据库查询没有返回任何行
- 为什么没有任何行? - 用户 ID 不正确
- 为什么用户 ID 不正确? - 路由参数解析失败
根本原因: 路由参数解析未处理边界情况。
步骤 9.2:检查系统依赖项
验证环境和依赖项。
precision_exec:
commands:
- cmd: "node --version"
- cmd: "npm --version"
- cmd: "npm list --depth=0"
verbosity: minimal
常见依赖问题:
- 版本不匹配(package.json 与 lockfile 不一致)
- 对等依赖冲突
- 已知存在错误的过时软件包
- 缺少原生依赖项
步骤 9.3:查看近期更改
查找错误出现前发生的更改。
precision_exec:
commands:
- cmd: "git log --oneline -10"
- cmd: "git diff HEAD~5..HEAD"
verbosity: standard
使用 Git bisect 处理复杂问题:
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Test, then mark:
git bisect good # or bad
# Repeat until culprit commit found
阶段 10:预防——避免未来出现问题
添加防护措施,防止问题再次发生。
步骤 10.1:添加测试
编写能够捕获错误的测试。
import { describe, it, expect } from 'vitest';
describe('getUserName', () => {
it('should handle undefined user', () => {
expect(getUserName(undefined)).toBe('Unknown');
});
it('should handle user without name', () => {
expect(getUserName({ id: '123' })).toBe('Unknown');
});
it('should return user name', () => {
expect(getUserName({ id: '123', name: 'Alice' })).toBe('Alice');
});
});
步骤 10.2:添加日志
记录关键决策点,以便日后调试。
import { logger } from '@/lib/logger';
function processUser(user: User | undefined) {
if (!user) {
logger.warn('User is undefined in processUser');
return null;
}
logger.info('Processing user', { userId: user.id });
try {
// Process user
} catch (error: unknown) {
logger.error('Failed to process user', { userId: user.id, error });
throw error;
}
}
日志最佳实践:
- 使用结构化日志(JSON)
- 包含上下文(用户 ID、请求 ID)
- 使用适当的日志级别(error、warn、info、debug)
- 绝不记录敏感数据(密码、令牌)
- 使用关联 ID 跟踪请求
步骤 10.3:添加监控
跟踪生产环境中的错误。
错误跟踪(Sentry、Rollbar):
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.1,
beforeSend(event, hint) {
// Filter sensitive data
if (event.request) {
delete event.request.cookies;
}
return event;
},
});
自定义错误类:
export class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500,
public context?: Record<string, unknown>
) {
super(message);
this.name = 'AppError';
}
}
throw new AppError('User not found', 'USER_NOT_FOUND', 404, { userId });
常见调试模式
详细模式请参见按类别整理的 references/debugging-patterns.md。
错误模式检测
使用 discover 一次查找多种错误模式。
discover:
queries:
# Type safety issues
- { id: any_usage, type: grep, pattern: ':\\s*any', glob: '**/*.{ts,tsx}' }
- { id: type_assertions, type: grep, pattern: 'as (any|unknown)', glob: '**/*.{ts,tsx}' }
# Runtime safety
- { id: unsafe_access, type: grep, pattern: '\\.[a-zA-Z]+(?!\\?)', glob: '**/*.{ts,tsx}' }
- { id: unhandled_promises, type: grep, pattern: 'await.*(?!try)', glob: '**/*.{ts,tsx}' }
# Performance
- { id: n_plus_one, type: grep, pattern: 'for.*await.*prisma', glob: '**/*.{ts,tsx}' }
- { id: inline_objects, type: grep, pattern: 'onClick=\\{\\{', glob: '**/*.{tsx,jsx}' }
verbosity: locations
系统化错误分析
步骤 1:对错误分类
- 类型错误(编译时)
- 运行时错误(null、undefined、类型不匹配)
- 逻辑错误(行为错误,但不会抛出异常)
- 性能错误(速度过慢)
- 网络错误(API 调用失败)
步骤 2:查找相似模式
- 搜索错误消息
- 搜索错误类型
- 搜索出错的函数名称
步骤 3:追踪数据
- 数据来自哪里?
- 发生了哪些转换?
- 数据在哪里出错?
步骤 4:验证假设
- 数据结构是否正确?
- 类型是否准确?
- 是否处理了边缘情况?
步骤 5:修复并验证
- 应用修复
- 运行类型检查
- 运行测试
- 手动验证
精准调试工具
Discover 工具
并行搜索以查找错误模式。
discover:
queries:
- id: error_sites
type: grep
pattern: "throw new"
glob: '**/*.{ts,tsx,js,jsx}'
- id: catch_blocks
type: grep
pattern: "catch\\s*\\("
glob: '**/*.{ts,tsx,js,jsx}'
- id: console_logs
type: grep
pattern: "console\\.(log|error|warn)"
glob: '**/*.{ts,tsx,js,jsx}'
verbosity: locations
Precision Grep
搜索时显示上下文,以便理解周边代码。
precision_grep:
queries:
- id: error_context
pattern: "TypeError|ReferenceError|RangeError"
glob: "**/*.{ts,tsx,js,jsx}"
output:
format: context
context_before: 5
context_after: 5
verbosity: standard
Precision Exec
运行诊断命令。
precision_exec:
commands:
- cmd: "npm run typecheck 2>&1"
- cmd: "npm run lint 2>&1"
- cmd: "npm test 2>&1"
verbosity: standard
Precision Edit
以原子方式应用修复。
precision_edit:
edits:
- path: "src/utils/user.ts"
find: "user.name"
replace: "user?.name ?? 'Unknown'"
verbosity: minimal
验证脚本
使用 scripts/validate-debugging.sh 验证调试实践。
./scripts/validate-debugging.sh /path/to/project
脚本会检查:
- 是否已从生产环境中移除 console.log 语句
- 是否已实现错误边界
- 是否已配置源映射
- 是否使用了合适的日志库
- try/catch 块是否处理了错误
- 是否已从生产环境中排除调试依赖
快速参考
调试检查清单
复现:
- 错误可以稳定复现
- 已确定最简复现步骤
- 环境一致
- 已捕获错误消息
隔离:
- 已分析堆栈跟踪
- 已追踪数据流
- 已测试边界条件
- 已找到相似模式
识别:
- 已对错误分类
- 已识别根本原因(而非表面症状)
- 已验证假设
- 已检查依赖项
修复:
- 已选择合适的修复方案
- 已添加防御性代码
- 已应用修复
- 未引入新问题
验证:
- 类型检查通过
- 测试通过
- 已完成手动验证
- 已测试边缘情况
预防:
- 已针对该问题添加测试
- 已添加日志
- 已配置监控
- 已更新文档
错误严重性指南
严重(立即修复):
- 应用崩溃
- 数据丢失或损坏
- 安全漏洞
- 生产环境中断
高(尽快修复):
- 功能完全失效
- 用户体验差
- 性能下降
- 违反类型安全
中(有条件时修复):
- 边缘情况失败
- 轻微的用户体验问题
- 控制台警告
- 缺少错误处理
低(之后修复):
- 代码风格问题
- 缺少文档
- 优化机会
- 重构需求
应避免的常见错误
修复症状,而非原因:
- 错误:添加 null 检查,却不弄清它为什么是 null
- 正确:追踪该值为何为 null,并修复源头
跳过验证:
- 错误:应用修复后就假定问题已解决
- 正确:运行测试、类型检查并进行手动验证
忽略边缘情况:
- 错误:只修复常见情况
- 正确:测试 null、undefined、空值和大值
不做预防:
- 错误:修复 Bug 后就继续往下做
- 正确:添加测试和日志以防止问题再次发生
高级技巧
二分调试
对于复杂问题,使用二分法缩小范围。
注释掉一半代码:
- 注释掉函数的一半
- 如果错误仍然存在,问题就在剩余部分
- 如果错误消失,问题就在被注释的部分
- 重复此过程,直到定位问题
橡皮鸭调试法
大声讲解问题:
- 描述代码应该做什么
- 描述代码实际做了什么
- 逐行梳理代码
- 讲解过程中通常就能发现问题
时间回溯调试
使用 git 查找 Bug 是何时引入的:
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Test each commit
git bisect good # or bad
日志策略
追踪重要值:
function processData(data: unknown) {
logger.debug('Input data', { data });
const validated = schema.parse(data);
logger.debug('Validated data', { validated });
const transformed = transform(validated);
logger.debug('Transformed data', { transformed });
return transformed;
}
使用关联 ID:
import { v4 as uuid } from 'uuid';
export async function POST(request: Request) {
const requestId = uuid();
logger.info('Request started', { requestId });
try {
const result = await processRequest(request);
logger.info('Request completed', { requestId });
return Response.json(result);
} catch (error: unknown) {
logger.error('Request failed', { requestId, error });
throw error;
}
}
与其他技能的集成
- 使用 error-recovery 尝试自动修复
- 使用 code-review 在审查过程中发现错误
- 使用 testing-strategy 防止回归
- 使用 performance-audit 处理性能问题
- 使用 security-audit 查找安全漏洞
资源
references/debugging-patterns.md- 按类别整理的常见错误模式scripts/validate-debugging.sh- 自动验证调试实践- Chrome DevTools - https://developer.chrome.com/docs/devtools/
- React DevTools - https://react.dev/learn/react-developer-tools
- Node.js 调试指南 - https://nodejs.org/en/docs/guides/debugging-getting-started/