先承认一件事:我编程快八年了,但到今天也说不清编程思维到底有什么官方定义。网上总有人把它包装成这么一套:遇到问题先拆解,拆成小块再逐个击破。这个说法对吗?对,但只对了一半。就像你急着出门,知道要把出门拆成“穿衣、带钥匙、穿鞋”,可你到了门口才发现,钥匙已经掉沙发缝里了。而且你穿鞋时发现左脚鞋带断了。这些破事,才是你真正需要处理的东西。
我自己的经历是这样:前两年接了一个小活,把客户发的Excel按“门店”拆成独立文件。我第一反应是清清爽爽地写三个步骤:读表、分组、输出。逻辑上完美吧?结果第一版跑起来卡了,客户文件里有几个隐藏工作表、有合并单元格、有空行,还有一列日期被Excel自动转成了东八区时间戳。那一下午,我花了大概四个小时处理各种边界条件和例外。那些才是真实世界的程序。回头再看,编程思维的能力,并不是拆解得有多漂亮,而是你有多大本事在面对“这里怎么会有这种东西?”的时候,不慌不忙地把脏地方擦干净。
所以我越来越觉得,编程思维本质上是“面向失败的思维”。普通解决问题的思路是:我怎么样才能让它工作?而程序员的内心戏是:它在什么地方会炸?我需要提前埋什么防线?新手喜欢一路写完然后满怀期待地运行,老手写两行就要打印一下看看,生怕环境跟他开玩笑。这不是神经质,是被教训过。我记得早年我写过一段循环,明明只是取商品价格再乘数量,结果数值全飘了——因为数据库的 price 字段是浮点数,0.1乘以3不等于0.3。这种细节刻在脑子里之后,你再写代码会下意识地拒绝使用浮点数存金额。这种改变不是知识变多了,而是一种身体的判断力,就像吃辣被呛过之后,看见红色会先停一下。
都说编程思维是逻辑思维,我特别不同意。逻辑思维是理想化的,前提干净,推论严密。而程序是寄生在物理世界里的,它跑在硬盘、网线、各种第三方库和同事写的垃圾代码上面,什么怪癖都有。有一次我们后台凌晨报错,日志显示查询顺序时对时错。我折腾了两个小时,最后查到是因为那个数据库配置了并行查询,默认返回顺序不被保证,而项目代码里没有任何 ORDER BY。书上的排序是稳定的、确定的,真实世界的排序是会背叛你的。这种问题靠纯逻辑推不出来,你得有对系统物理性的直觉,类似于你知道网络可能会抖,知道时间戳不是你想的那个时区,知道“删除”是“标记删除”不是物理删除。这些才是一个程序员真正赖以生存的“生活常识”。
如果问怎么培养这种思维,我不会劝你天天去刷题。刷题练的是数学,不是编程。编程思维只能在真问题里长出来。我推荐一个笨方法:把你自己六个月前写的代码打开,试着加个新功能,或者修一个以前被你遗留的bug。你会无比生动地看见自己当初的愚蠢,那种“当时的我怎么会用这种方式遍历列表”的羞耻感,会逼着你思考如何设计才能让以后的自己少受点罪。如果你觉得不够刺激,就去一些开源项目里帮人修issue,尤其是那些星标不高、代码丑但有人在用的老项目。修过的坑越多,你脑里的“常见坏法清单”就越厚,之后看代码一眼就能感觉到哪里会出事。我见过数学竞赛厉害的人写不出能跑的程序,因为他活在一个没有脏数据的纯净世界里,而程序是一片沼泽。编程思维没有多玄学,它只是你被现实坑过千百次之后,建立的肌肉记忆。