程序这东西,跑起来不出错是理想,出错没人慌是本事。我做了十来年开发,见过太多项目上线前被一个诡异的报错卡住,也见过线上系统半夜三点崩溃、值班同事对着日志一脸懵。程序错误处理看着是个“基础话题”,但真正能把它做明白的人不多——大多数人的水平停留在“报错就百度,百度不到就重启”。这篇内容我想把自己这些年积累的经验梳理一遍,从“怎么看待错误”到“怎么定位错误”,再到“怎么用代码把错误处理得优雅”,一次讲透,适合刚入行的开发者,也适合带团队的技术负责人参考。
1. 错误处理的思维框架:先分清楚错误的“物种”
1.1 错误不等于故障,先更新观念
很多人一听到程序报错就头皮发麻,觉得是程序出了大问题。我这些年最大的体会是:错误是程序运行的一部分,就像车辆行驶会遇到红灯一样正常,真正决定系统质量好坏的关键,不是你写了多少“不会出错的代码”,而是当错误发生时,你能用多快的速度定位、恢复、避免再次发生。
举个例子,你写了一个 PHP 接口,用户传了个空字符串导致 SQL 拼接出问题,数据库报了个语法错误。这个错误本质上不是“数据库坏了”,而是你的代码缺少对输入的校验。如果不把错误分类,一上来就去查数据库配置文件,那大概率是白忙活。所以处理错误的第一步不是动手改代码,而是先搞清楚:这个错误属于哪一个物种?
我习惯把程序运行中遇到的所有错误分成四类:
- 环境类错误:路径不存在、命令找不到、端口被占用、权限不足、依赖未安装。这类错误的特点是换一台机器或换一个环境就变了,跟你代码逻辑基本无关。
- 逻辑类错误:空指针、数组越界、类型不匹配、状态机跳错了分支。这类错误是代码自身的缺陷,需要靠 review 和测试来兜底。
- 资源类错误:内存溢出、句柄泄漏、连接池耗尽、磁盘写满。这类错误通常是长期运行才暴露出来的,具有“积累性”和“延迟性”。
- 外部依赖错误:上游接口超时、第三方服务返回异常、数据库连接断开。这类错误的特征是——你控制不了对方,只能控制自己怎么应对。
这四类错误的排查思路完全不同。环境类错误先查配置和环境变量,逻辑类错误去看堆栈和代码走查,资源类错误要盯监控指标做趋势分析,外部依赖错误则要把超时、重试、降级策略做好。后面几章我分别展开细讲。
1.2 防御式编程:不要假设环境是干净整洁的
我刚工作的前两年,写代码有一个特别不好的习惯:默认环境是好的、输入是合法的、第三方接口是稳定的。这种乐观主义害我不浅。后来吃了几次大亏,才明白防御式编程的核心并不是“怕出错”,而是“对现实保持敬畏”。
怎么理解?就拿网上很热门的那个报错来说——pnpm' 不是内部或外部命令,也不是可运行的程序或批处理文件。很多新手的第一反应是“pnpm 坏了”,实际上呢?是安装路径没加到系统 PATH 环境变量里。这就是典型的“环境不干净”问题。你本地开发环境装了一堆工具,全局变量、路径、版本各种纠缠,稍不注意就出现命令找不到的情况。
防御式编程落到实操上,就是三个“永远不要”:
- 永远不要假设用户的输入是合法的。“我记得这里不会传空”这种话我听过太多次了,每次说完没几天就翻车。正确做法是入口处统一做参数校验,非法输入直接拦截并返回明确的错误码。
- 永远不要假设依赖的组件是可用的。数据库连接池要设置最大连接数和等待超时,HTTP 调用要设置连接超时和读取超时,文件操作要判断返回值。别把“大概率能用”当成“一定能用”。
- 永远不要假设异常分支会被测试覆盖。很多团队只测“正常路径”,异常路径全靠运行时随机踩坑。至少要保证主要的 error branch 有日志输出和有明确返回,不然线上出了事,你连从哪查起都不知道。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高频环境类错误排查:命令找不到、连接失败、权限不够
2.1 PATH 相关报错:npm、pnpm、git 无法识别的完整处理方案
这两年在技术社区里,关于命令无法识别的求助帖特别多。比如:
npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称pnpm' 不是内部或外部命令,也不是可运行的程序git : 无法将“git”项识别为 cmdlet、函数、脚本文件或可运行程序的名称claude : 无法将“claude”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
看多了你会发现,这些报错其实是一家人——命令行解释器在 PATH 环境变量所列的目录里找不到对应的可执行文件。原因无非以下三种:
- 软件安装时没有勾选“加入 PATH”选项,比如 Windows 上手动解压的 Node.js 或设置了“仅为当前用户安装”的 Git。
- 安装没有问题,但是终端会话还是旧的环境变量快照,需要重开一个终端窗口。
- 用 nvm-windows 或 fnm 这类版本管理工具切换过 Node 版本,但当前版本目录下确实没有 pnpm(因为 pnpm 是全局装在另一个版本里的)。
排查顺序我建议是“先看后改”:
第一步:确认命令对应的可执行文件存在
Windows 下可以打开 PowerShell,执行:
powershell复制# 查看 npm 的实际路径
Get-Command npm -ErrorAction SilentlyContinue
# 或者直接在常见安装路径里找
Test-Path "C:\Program Files\nodejs\npm.cmd"
Test-Path "$env:APPDATA\npm\pnpm.cmd"
如果文件在磁盘上存在,但命令就是无法识别,那就是 PATH 的问题。如果文件不存在,那就是安装的问题,重新装一遍或者用包管理器装。
第二步:查看当前 PATH 环境变量
powershell复制# PowerShell 查看 PATH
$env:Path -split ';'
重点看有没有包含 C:\Program Files\nodejs\,以及全局 npm 包目录 %APPDATA%\npm。正常安装的 Node.js 会自动把这两个目录加进系统 PATH。
第三步:手动修复
假设你用的是 nvm-windows 管理多个 Node 版本,切到某个旧版本后发现 pnpm 不见了,最省事的方式是重新执行:
powershell复制# 已有 lockfile 的情况下安装依赖
corepack enable pnpm
或者干脆回到原来装了 pnpm 的 Node 版本再使用。环境问题最忌讳的就是“硬刚”,顺着版本管理工具的机制走,往往比手动改 PATH 更靠谱。
注意:修改系统环境变量 PATH 后一定要重开终端窗口。很多“改了不生效”的案例,其实只是终端缓存了旧的 PATH,并不是改坏了。
2.2 连接类与权限类报错:VMware、扩展程序这类问题怎么处理
环境错误不止命令找不到一种,连接失败和权限不足同样是高频问题。
先看 vmware workstation 无法连接到虚拟机。请确保您有权运行该程序、访问该程序使用的所有目录。这类报错我在帮同事排查时遇到过很多次,原因往往集中在三个点:
- VMware 的后台服务没起来。Windows 下可以按
Win + R输入services.msc,找到VMware Authorization Service,确认状态是“正在运行”。如果没运行,右键启动,并把启动类型设为“自动”。 - 虚拟机文件所在目录的权限不对。VMware 需要用管理员权限读取
.vmx文件,尤其是放在系统盘C:\Users\xxx\Documents\Virtual Machines下的虚拟机,目录被访问控制列表(ACL)限制会导致连接失败。解决方法是右键虚拟机安装目录,进入“安全”标签,给当前用户授予完全控制权限。 - 虚拟机被别的进程占用。比如你开了两个 VMware 窗口,或者后台还残留
vmware-vmx.exe进程没退干净,把残留进程结束后再试试。
再看浏览器报错:无法安装扩展程序,因为它使用了不受支持的清单版本。这个错误背后的原因通常是扩展的 manifest.json 里写了过旧的 manifest_version(比如 1),而现代浏览器只支持版本 2 和版本 3。处理方式有三条路可走:
- 升级扩展:去 Chrome 网上应用店或 Edge 加载项商店搜一个替代或新版。
- 如果是自己写的扩展,把
manifest.json里manifest_version改成3,同时注意扩展脚本从后台页迁移到 service worker。 - 如果是公司内部的旧扩展,直接联系开发团队改造,别自己硬改。
这类权限和连接问题的共同点在于:错误信息本身只是表象,真正的根因往往藏在系统服务、目录权限和进程占用里。遇到此类报错,先别急着重装软件,把“服务状态、权限设置、进程占用”这三板斧走一遍,大多数问题都能定位到。
2.3 外壳程序崩溃:explorer.exe 反复重启怎么办
Windows 用户可能遇到过这样一个提示:外壳程序意外停止,explorer.exe 被重新启动。这个是 Windows 资源管理器崩溃后的自恢复行为,本身是系统保护机制在工作,但也侧面说明你的系统环境有隐患。
我处理过几次,最常见的原因是第三方壳扩展(Shell Extension)和资源管理器冲突。比如安装了某些网盘客户端、压缩软件、右键菜单增强工具之后,explorer 会经常崩。排查思路:
- 打开事件查看器,在“Windows 日志 > 应用程序”里筛选来源为
Application Error的事件,看崩溃的模块名是什么。如果是某个*.dll,那八成就是它干的。 - 用
shellExView这类工具禁用可疑的右键菜单扩展,一个一个排除。 - 临时修复可以用任务管理器重启资源管理器,右键“任务栏”->“任务管理器”-> 找到“Windows 资源管理器” -> 右键“重新启动”。
如果崩溃频率很高,建议直接进入“干净启动”模式(msconfig 里关闭非微软服务)验证,基本就能锁定元凶。
3. 运行时崩溃与异常捕获:从日志到源码的完整回溯
3.1 日志:错误处理的第一道防线,也是最容易被忽略的防线
我在团队里经常说一句话:没有日志的错误,等于没有发生。你想想,线上系统报了一个错,如果你连“错误发生在哪个模块”“入参是什么”“返回值是什么”都不知道,怎么定位?所以日志是错误处理的地基,地基没打好,后面一切排查技巧都是纸上谈兵。
但日志不是随便打印几行就完了。优秀的日志需要做到三件事:
- 有级别:
DEBUG拍给开发看,INFO记录业务流程,WARN提示潜在风险,ERROR表示需要人工介入。很多团队把WARN和ERROR混着打,告警一多,人就会麻木,反而漏掉真正的问题。 - 有上下文:单打一条“ERROR: 连接失败”没有任何意义。要把关键参数打出来,比如“连接 Redis 超时,host=10.0.0.8:6379, timeout=3000ms”。有了上下文,一看日志就知道是在哪个环节出的问题。
- 可追踪:分布式系统里一个请求会经过多个服务,每跳一层都要带一个
traceId,这样查日志时才能把链路串起来。如果你们团队还没有接入全链路追踪,至少要在应用层自己生成一个请求 ID。
拿 PHP 项目举例,很多人调试时直接在代码里 var_dump 一把梭,上线前忘了删。这种做法在开发环境无伤大雅,一上线就是灾难。正确做法是在入口处统一注册异常处理函数:
php复制set_exception_handler(function ($e) {
error_log(sprintf(
"[%s] %s in %s:%d\n%s",
date('Y-m-d H:i:s'),
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
), 3, '/var/log/php/app_error.log');
});
同时在 PHP 配置文件里把 display_errors 关闭,把 log_errors 打开。开发环境可以开 display_errors 方便调试,但生产环境一旦开着,用户就会看到一堆不友好的报错信息,甚至暴露服务器绝对路径,安全上也说不过去。
3.2 崩溃现场:core dump、栈回溯与分析工具
日志记录的是“错误发生了”,但有些场景——比如 C/C++ 程序段错误(Segmentation Fault)、Qt 程序崩溃——日志里可能只有一个退出码,什么都不剩。这时候就要靠崩溃现场来分析。
我以前调试过一个 Qt 桌面程序,用户反馈“点某个按钮就闪退”,但日志里没有任何异常信息。后来让测试环境复现,再用调试器抓栈回溯,一下子就定位到问题:某个控件在使用前没有判空,导致访问了空指针。这类问题的排查套路如下:
第一步:打开 core dump
Linux 下默认不生成 core dump 文件,需要手动设置:
bash复制# 临时开启 core dump,unlimited 表示不限制大小
ulimit -c unlimited
# 查看 core dump 生成位置
cat /proc/sys/kernel/core_pattern
第二步:用 gdb 分析现场
bash复制gdb ./your_program core
进入 gdb 后执行 bt 查看函数调用栈,回车后你就知道程序在哪个函数崩了。如果是 release 版本,函数名可能是地址而非符号,建议编译时带上 -g 调试选项,发布时再裁剪,或者单独保存符号表文件。
第三步:如果是 C/C++,用 AddressSanitizer 提前发现内存问题
用 -fsanitize=address 编译,运行时如果发生越界、悬空指针,它会直接打印出出错的代码行和分配/释放时的调用栈,比事后分析 core dump 效率高得多。我自己的经验是:能用 ASan 的尽量在测试阶段跑一遍,很多崩溃问题在测试环境就能暴露,别等上线后去复盘。
第四步:Windows 平台可以用事件查看器 + dump 文件
Windows 上可以在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps 里配置 dump 文件的生成目录和大小限制。程序崩溃后,用 WinDbg 打开 dump 文件,执行 !analyze -v,它会自动帮你分析这个崩溃的异常类型和调用栈,相当好用。
补充一个经验:程序“崩溃”前往往有预兆——内存持续上涨、句柄数不断增多、某个接口的耗时不时超时一下。这些指标比崩溃本身更值得关注,因为等崩溃了再去抢救,已经是事后诸葛亮了。
3.3 小程序商城的“隐藏故障”:支付失败与接口报错的排查思路
小程序这几年是开发重灾区。原因倒不是小程序本身多难写,而是它场景复杂——涉及微信生态、第三方支付、H5 跳转、前端渲染、后端接口,随便一层出问题,用户感受到的都是“小程序打不开”“支付不了”。
从热搜词里也能看到一堆典型的:小程序商城、小程序微信支付v3对接 由于小程序违规,支付功能暂时无法使用、小程序跳转h5页面、微信小程序抓包。
我最想多说两句的是支付类错误。微信支付v3 对接时最常见的问题有:
- 证书或密钥不匹配:商户私钥、平台证书、APIv3 密钥三者不匹配,请求时微信服务端会直接返回
签名错误。处理办法是重新下载证书,用微信支付官方提供的证书工具生成密钥对,确认商户号与 AppID 的绑定关系。 - 回调验签失败:微信支付成功后回调你的服务器,如果验签逻辑写错,回调就会被忽略,用户付了钱但订单状态不变。排查时先看回调日志,确认有没有收到微信的通知,再用微信支付 SDK 的验签方法做校验。
- 权限受限:比如提示“由于小程序违规,支付功能暂时无法使用”,这类是平台处罚,技术上无解,只能去小程序后台查看违规记录并申诉。
至于接口层面的错误,我强烈建议在小程序后台开启“开发环境不校验合法域名”,但生产环境一定要把接口请求的 request 封装统一处理——统一做超时控制、统一拦截业务错误码、统一上报异常日志。
如果遇到的是用户侧“页面空白但接口正常”这种诡异问题,记得用开发者工具的 Network 面板看看请求有没有被拦截,用真机调试跑一遍,很多小程序白屏问题只在特定机型上能复现。
4. 代码层面的错误处理策略,直接决定系统能扛多大事
4.1 错误码设计:别再用“0”和“1”代表所有情况
有一次评审同事的代码,发现他把所有函数的返回值都设计成 0 成功、1 失败。我问他:那“参数错误”和“服务不可用”你怎么区分?他说都返回 1。这就导致排障时必须去翻日志猜原因,效率极低。
好的错误码设计应该具备“自解释”能力。我比较推荐的做法是“模块号 + 错误类型 + 具体编号”的三段式结构:
| 错误码 | 含义 |
|---|---|
| 10001 | 通用参数校验失败 |
| 10002 | 用户不存在 |
| 20001 | 订单状态不允许当前操作 |
| 20002 | 订单支付超时 |
| 30001 | 外部支付服务不可用 |
| 30002 | 外部支付服务返回数据异常 |
这样前后端联调的时候,后端返回 20002,前端直接弹“订单支付超时”,同时前端埋点上报这个错误码,产品运营也能看懂。比起“后端返回了一个 -1,前端显示系统繁忙”这种处理方式,不知道高到哪里去了。
错误码还有一个附加价值:可以做告警聚合。同一类错误码短时间内激增,说明它在集中发生。比如 30001 一小时出现 500 次,那基本可以断定支付渠道出了问题,可以触发自动告警甚至自动降级。
4.2 try/catch 的边界感:捕获、转译、抛出、重试
很多新手写代码有一个坏习惯:空 try/catch,捕获异常后什么都不做,或者只打一句话。用我见过最极端的话说——“吞掉异常等于给系统埋雷”,你当时图省事没处理,后面线上出了问题,排查成本翻好几倍。
我自己写代码时对 try/catch 有个基本原则:能精确捕获就精确捕获,能往上抛就往上抛,只有在这个地方确实能兜住且能给出替代方案时,才在本地处理。
举个例子,一个 Node.js 应用调用外部 API 获取天气数据,你可能面临三种情况:
- 超时了,可以重试一次,所以在这个环节做重试逻辑,返回“获取失败,请稍后再试”。
- 返回的数据格式不对,这个重试也没用,应该记录日志并向上抛出,让上层决定是降级还是给用户一个默认值。
- 依赖的服务直接挂了,如果本地有一个缓存的上一次数据,可以降级返回缓存。
把这些逻辑写成代码就是:
javascript复制async function getWeather(city) {
try {
const data = await httpGet(`/api/weather/${city}`, { timeout: 3000 });
return parseWeather(data);
} catch (err) {
if (err.name === 'TimeoutError') {
// 超时可重试一次
log.warn(`weather api timeout, retry once, city=${city}`);
return retry(() => httpGet(`/api/weather/${city}`, { timeout: 3000 }));
}
if (err.name === 'InvalidResponse') {
// 数据格式不对,无法重试,向上抛
throw new BusinessError('WEATHER_DATA_INVALID', city);
}
// 其他错误,走降级:返回上次缓存数据
return cache.get(`weather:${city}`) ?? fallbackWeather;
}
}
这种写法最关键的地方是把“可重试”“不可重试”“可降级”三种情况拆开了,各有各的处理方式,不会一锅乱炖。
重试机制本身也有讲究——线上系统最忌讳的就是“失败后立刻疯狂重试”,这会给下游服务造成二次压力。比较规范的做法是指数退避 + 抖动:
javascript复制function sleepWithBackoff(attempt) {
const base = Math.min(1000 * Math.pow(2, attempt), 10000);
const jitter = Math.random() * 500;
return new Promise((resolve) => setTimeout(resolve, base + jitter));
}
第一次失败等 1 秒左右,第二次等 2 秒左右,第三次 4 秒……最后封顶 10 秒,同时加一点随机抖动防止多个请求同时重试造成“惊群效应”。还有一点很重要——任何重试都必须设总次数上限,别让一个必败的请求无限重试下去,既浪费资源又拖垮用户。
4.3 让失败被看见:错误监控与告警的落地姿势
代码写得再好,如果错误发生后你根本不知道,那等于没处理。所以我一直强调“错误处理”不只发生在代码里,更发生在监控告警里。
这里我推荐一个“四级告警”实践:
- 最低级别:单次失败,不告警,只在日志里记录。
- 低级别:某个错误码 5 分钟内出现超过 20 次,发送工作群消息提醒。
- 中级别:某个接口错误率超过 5% 且持续 10 分钟,电话通知责任人。
- 高级别:核心业务接口不可用(错误率 100%),立即拉群、发短信、启动应急预案。
注意,告警不是“越多越好”,告警越多,人的注意力越容易被稀释,最后全变成“狼来了”。宁可告警少一点,但每一条都是有效告警。要把告警和日志联动起来,告警消息里带上 traceId、错误码、受影响用户数,让收到告警的人能在 1 分钟内开始排查,而不是看到一条“有错”又得自己翻半天日志。
5. 常见问题与排查技巧实录
5.1 高频报错速查表
把上面讲的知识点浓缩成下面这张速查表,线上出问题时对照着查,能省不少时间:
| 报错现象 | 可能原因 | 首选排查步骤 |
|---|---|---|
| npm/pnpm/git 不是内部或外部命令 | PATH 未配置或安装不完整 | Get-Command npm,重开终端,重新安装或手动加 PATH |
| 无法将“claude”识别为 cmdlet | CLI 工具未安装或未加入 PATH | 确认安装目录,重新执行安装脚本或手动添加 PATH |
| 无法连接到虚拟机 | VMware 服务未启动 / 目录权限不足 | 检查 VMware Authorization Service,确认 .vmx 文件目录权限 |
| 无法安装扩展,不支持清单版本 | manifest_version 过旧 | 更新扩展或修改 manifest_version 为 3 |
| explorer.exe 被重新启动 | 第三方 Shell 扩展冲突 | 查看事件查看器崩溃模块,禁用可疑扩展 |
| PHP 页面报 500 但没有错误信息 | display_errors 关闭 | 查看 PHP error_log,或临时开 display_errors 定位 |
| 小程序支付报“签名错误” | 证书/密钥配置不对 | 核对商户号、AppID、APIv3 密钥、平台证书 |
| SQL 语法错误但有拼接痕迹 | 输入未做校验导致注入 | 打开错误日志看完整 SQL,改用参数绑定 |
| 程序崩溃后没有日志 | 没有全局异常处理 / core dump 未开 | 注册全局异常处理器,开启 core dump |
| 接口偶发超时 | 外部依赖抖动 / 连接池耗尽 | 看超时时间和错误码分布,设置重试和降级 |
这张表是我这几年踩坑后的经验沉淀,很多问题其实第一眼看上去都不一样,但归类到上面的“四类错误”之后,处理路径就清晰很多。
5.2 几个独家排查技巧,关键时刻能救命
最后分享几个常规文档里不怎么会写、但实际排查效率极高的技巧。
第一个技巧:日志倒查法
当一个线上问题没有任何头绪时,先别急着看源码。把出问题的时间点找出来,然后去日志系统里查那个时间点前后 5 分钟内的所有 ERROR 和 WARN,通常肇事者就藏在里面。我靠这个方法解决过至少几十个看似毫无规律的问题,包括一次 RabbitMQ 消费者莫名消失、一次数据库连接数打满、一次微信回调丢失。
第二个技巧:二分法定位环境问题
如果你在排查一个“换了环境就出错”的问题,可以用二分法缩小变量范围:先拿一台正常机器,逐步把异常机器的环境变量、配置文件、依赖版本复制过来,每复制一项跑一次测试,很快就能找到是哪个变量引发的。这个方法虽然原始,但我亲测有效,比对着配置逐行猜快得多。
第三个技巧:给你的调试器加“现场保护”
调试 C/C++ 程序偶尔会遇到这样一个尴尬:程序一跑就崩,但崩的时候你还没来得及挂上调试器。解决方法是让程序崩溃时自动等待调试器——Linux 下可以在崩溃处理函数里 while(!ptrace(PTRACE_TRACEME, 0, 1, 0)) sleep(1); 加上一个循环等待,然后用 gdb attach 上去看现场。这个技巧比较 Geek,但关键时刻真能救命,不至于一个崩溃问题拖一整个晚上。
第四个技巧:外包系统报错先问“你改了什么”
还有一种“玄学”错误——环境昨天还好好的,今天突然就不行了。这种问题 90% 以上是“有人偷偷改了配置”或者“有进程把资源占满了”。遇到这种情况,第一件事不是去分析代码,而是问团队:昨天到今天有没有人动过服务器、改过配置、升级过依赖?问完之后再配合日志和监控去看,往往十分钟就能定位。
写在最后:错误处理是工程能力的分水岭
我招人的时候喜欢问候选人一个问题:你遇到过一个印象最深的线上故障吗?是怎么处理的?问这个问题不是因为我想听某个具体技术方案,而是想看他面对错误时的状态——是慌乱、逃避,还是能冷静地把问题拆解、定位、解决。
做了这么多年开发,我愈发觉得,代码写得快不算什么本事,能把错误处理得清清楚楚、让系统在故障中依然有序运行,才是真正拉开差距的地方。程序错误处理不是晦涩的技术理论,它是一套思维方式:往前想一步,考虑一下如果这里挂了怎么办;往后兜一手,保证异常发生时系统不会整个崩溃;最后留一双眼,让所有的失败都能被及时看见。
如果你现在刚接触编程,不妨从今天开始养成一个好习惯:每次遇到报错,不只是把它“修好”,而是想一想它属于哪一类错误、为什么会出现、下次怎么在一分钟内发现它。等你把这些想明白了,你对程序的理解会上一个台阶,团队里的同事也会慢慢开始依赖你——因为你是那个不慌、不乱、总能解决问题的人。
