跳到主要内容

偶现 bug 排查思路

场景

QA 或用户反馈一个 bug,但你在本地无法复现,只是偶尔出现。怎么排查?

分析思路

排查框架

信息收集清单

维度要收集的信息
基本是什么问题?出现频率?操作步骤?
环境设备型号、操作系统、浏览器版本、网络环境
时间什么时候出现的?每次出现的时间点有规律吗?
用户特征所有用户还是特定用户?新/老用户?地区?
上下文之前做了什么操作?页面停留了多久?

常见偶现原因

1. 竞态条件(Race Condition)

❌ 接口返回顺序不确定
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<Item[]>([]);

useEffect(() => {
// 快速输入时,后发的请求可能先返回
fetch(`/api/search?q=${query}`)
.then((r) => r.json())
.then(setResults); // 旧请求覆盖新结果!
}, [query]);
}
✅ AbortController 取消旧请求
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<Item[]>([]);

useEffect(() => {
const controller = new AbortController();

fetch(`/api/search?q=${query}`, { signal: controller.signal })
.then((r) => r.json())
.then(setResults)
.catch((e) => {
if (e.name !== 'AbortError') throw e;
});

return () => controller.abort(); // 取消上一次请求
}, [query]);
}

2. 定时器 / 状态清理不彻底

组件卸载后异步操作才完成,更新了已卸载组件的状态。

3. 环境差异

  • 不同浏览器行为不同(日期格式、CSS 兼容性)
  • 不同时区导致时间计算错误
  • 移动端和桌面端事件模型差异

4. 数据边界

接口返回的数据中偶尔出现 null / undefined / 空数组,代码未做防御。

✅ 防御性编程
// 安全访问
const name = user?.profile?.name ?? '未知用户';
const firstItem = list?.[0] ?? defaultItem;

排查工具

工具用途
Sentry错误日志 + 面包屑 + 设备信息
LogRocket / rrweb用户操作录屏回放
自定义埋点关键操作路径记录
console.trace()追踪函数调用链
关键路径加日志
function checkout(order: Order) {
console.log('[Checkout] Start', { orderId: order.id, items: order.items.length });

try {
validateOrder(order);
console.log('[Checkout] Validation passed');
submitOrder(order);
console.log('[Checkout] Submitted');
} catch (error) {
console.error('[Checkout] Failed', { error, order });
reportError(error as Error);
}
}

常见面试问题

Q1: 偶现 bug 你一般怎么排查?

答案

  1. 收集信息:和反馈者确认环境、操作步骤、出现频率
  2. 分析可能原因:竞态条件?环境差异?数据边界?
  3. 尝试复现:模拟环境(浏览器、网络、数据)
  4. 加日志/埋点:如果无法复现,在关键路径加日志,等下次触发
  5. 录屏回放:接入 LogRocket / rrweb 等工具,还原用户操作
  6. Code Review:review 相关代码,寻找潜在问题

相关链接