很多人把调试看成编码之后的补救动作:功能写完了,发现不对,再开始找问题。这个理解太窄了。真正高效的调试能力,往往在写代码之前就已经开始了。

一个容易调试的系统,通常有几个共同点:边界清楚、状态可见、失败路径明确、日志能回答问题。反过来,如果一个模块输入输出含混、错误被吞掉、关键状态只能靠猜,那么调试就会变成漫长的试错。

我越来越倾向于把“以后怎么排查”当成设计的一部分。写接口时,先想清楚调用方出了错会看到什么;处理异常时,先想清楚日志里需要留下哪些上下文;设计状态机时,先想清楚每一次状态变化能不能被追踪。

一个很实用的习惯是:遇到 bug 时,不只是修掉当前问题,还要问一句:“为什么这个问题没有更早暴露?”如果答案是缺少测试,就补测试;如果答案是日志不够,就补日志;如果答案是抽象太绕,就考虑拆开。这样每次调试都会让系统变得更容易维护。

调试能力的上限,不只取决于你有多熟悉工具,也取决于代码是否愿意告诉你真相。好的工程设计,应该让问题尽快浮出水面,而不是把错误藏在层层封装后面。