方法论、方法论——程序员的阿喀琉斯之踵

以前,我认为一个事物对我没有直接用途的时候就不会去理会它,心理学上说我们都戴着自己的认知偏见的有色眼镜去有选择性地看待这个世界,纷繁的信息经过我们的认知图式过滤之后便成为少量有序的事件,所以我们都在有强烈选择性地关注一些事物和忽视另一些事物,然而,这样可能会导致丧失一些很有价值的信息,而总是将知识面停留在自己的小世界中——当然这倒也不是说看到什么都要凑上去学一学。如何在这两者中间取得折中,我觉得一个好的办法是先简略地想一下这是个什么东东,他的本质是什么,出现是为了满足什么需求,等等比较“高层”的问题(即“What”和“Why”而不是“How”),这些问题应该是可以通过简单的调研和思考得出结论的,至于背后的技术细节,如果你打算入行,就可以去学,如果不打算的话则可以免了,至少前面的思考和简单的调研能够一定程度上保证当有价值的信息或机会摆在你面前的时候你不会把眼睛蒙上走开,并且多做做这类思考对于思维的广度也很有价值。最近我开始认为,最佳的学习方法就是先广度优先遍历(先弄清What和Why),然后择最合适的分支深入(How)(算法牛人DD同学在TopLang上的一个帖子里面也提到类似的想法,刚进大学就能够如此清晰地看清前方道路的走法,我对DD很佩服)。

方法论看似是个很抽象的东西,并且的确有一些方法论是抽象到 over-generalized (泛化过度)的地步,然而说实话在实践当中我总是发现(正确的)方法论是再现实不过的东西,比如一个大家都明白的道理是:如果方向走错了,那么做的功就基本全白费了(还有比如“如果方法对头,就能事半功倍,反之可能多走很多弯路”)——然而现实中有多少人能够真正实践这个方法呢?绝大多数人都是只顾解决眼前问题,抓了这头丢了那头,更多人是不知道问题是什么,只管把头脑中能联想到的一个以前类似情况下的类似方案套用上来。以前我总是觉得一个公司里面,CEO/CTO 这样的角色是基本摆设,但我现在不这样想了。在 How 层面把事情做好,做成一个精钻的程序员,那顶多就是能把钳子使好,这样的事情很多人都能做到,熟能生巧嘛。换句话说程序员基本上是去解决一个定义好的问题,去实施一个定义好的方案。然而决策问题就不一样了,决策问题是需要去定义问题是什么,以及权衡最佳方案是什么,不管是决策技术架构还是决策商业策略,都是非常复杂的思维过程,需要综合和权衡大量的信息,这种能力就不是简单楞着头搞下去能练出来的了,很多时候需要抬起头来看,免得只见树木不见森林。(以上也是为什么我在讨论组里面一篇帖子(什么是算法?为什么学习算法?以及学到什么程度?)中提到我觉得学数学学到精通未必就会思考日常决策问题的原因——数学几乎总是去解决一个定义好的问题,用的也都是定义好的严密的逻辑推导。然而现实中的问题是一个复杂系统,诸多变量互相影响,如何权衡最佳方案实际上是一个复杂的统筹规划。更重要的是,你往往甚至都不知道问题是什么,能够从纷繁的信息中抽象出问题,是一种极大的能力。这里推荐《你的灯亮着吗?》《失败的逻辑》

当然,我自己还没能到这个层面,尚需要不断实践和总结,所以只能稍微的谈一点感受,再往下扯只怕就会流于空泛了。这一点上我还是举一个程序员们喜闻乐见的例子吧,在程序员眼睛里面,做一个项目,也许首先想到的是用什么语言,什么框架,什么库,在这个方向上那就是什么看上去牛B用什么,恨不能都用 haskell、lisp 来写才爽,用 Java?那多没意思啊,Java 那坨弱智语法我小学的弟弟都能掌握,也没啥牛B的语言特性,忒没成就感(只可惜真正判别弱智与否的并非用什么语言技术,而是做出什么产品满足什么需求)。这就是属于只考虑单个孤立因素的简单(或者说 Naive 的)决策,这个因素就是——只要让我自己感觉爽——只可惜并不是让自己感觉爽的做法就是真正解决问题的做法,始终要弄清问题是什么,在后者意义上,一些对于技术型程序员往往没有吸引力的话题其实有着极其重大的价值——比如什么时候设计,什么时候重构,什么时候集成,再往上一层其实这些又都是次级问题,首要的问题还是这个产品满足什么需求,有什么市场(即这件事情值不值得做),有一句话想必很多人常听说,如果不知道要做什么,套上十二层架构也无济于事,方法永远不是因,而是果(我在以前的另一篇文章“Failing to see the Big Picture – Mistakes we make when learning programming”中也阐述了类似的观点)。

再举个例子,如果我想给我的网站做一个 feature ,我认为这个 feature 技术上很牛很强大,而且刚好有机会使用一下我最近修炼的某某 framework 和某某语言,而且这玩意很有挑战性,还不是一般人能够做得了的,综合以上三点,我立时觉得心痒难耐摩拳擦掌。然而实际上这个问题应该怎样分析呢?首先,考虑到以上三点,这将会是一个投入相当大的项目,那么其收益就必须要对得起这个投入,技术上很牛不代表商业上就牛,再牛再难做的 feature 如果不能带来商业价值那就是负收益。总而言之,

1. 一件事情仅仅让你感觉挺牛不代表这件事情就是值得做的;
2. 一件事情仅仅让你感到很有兴趣并不代表这件事情就是值得做的。

这两句话和我们日常的认识并不冲突,其实我们几乎总可以找到既有价值、又有趣、又有足够挑战性的工作。举个例子,本科数学学得精纯无比的同学有没有偶尔也会觉得盲目呢?做这些题目到底有什么实际用途呢?这就像是你总是在磨一把刀,磨得闪闪发光锋利无比,你可以向别人炫耀自己的刀很牛B,但是刀是为了冲锋陷阵血溅五步的,你也不想让它折戟沉沙吧,不管是将数学用在数学物理上还是用在人工智能、机器学习、密码学、通信上,都是既让人有成就感,同时又有意义和价值的事情。对我们程序员来说,你把一门语言玩得很精通,不仅知晓它所有的语法细节,陷阱和缺陷,还了解它的底层实现模型是如何。你觉得很牛很有成就感——的确,我们都会为一件自己做到了别人做不到的事情而感到自豪,然而反问一句,除了情绪价值之外,这样的事情在本身的价值上有没有你感觉到的那么牛呢?如果你只是在削铅笔,那么何必磨一把倚天屠龙剑来?反之,如果你做的是一个本身功能很牛很创新很有价值的软件,那么语言技术其实完全是次要的,并不是看上去越眩越好,关键是选择各个方面综合考虑起来最合适的工具即可,瑞士军刀也许很丑,但对于丛林冒险很实用就行。拿着一把屠龙宝刀去野外生存,同样也不靠谱。

编程语言是为了实现软件的,软件是为了服务于人的。诚然,了解一门语言的方方面面能够使你更有效地使用它,然而另一方面,如果结果发现这门语言并不何时来解决你手头的问题呢?受到投入的沉没成本的影响你一定觉得很不甘心吧?同理,由于你对一门工具投入了很多的精力,这门工具已经和你的情感挂上了钩,于是如果让你来决策用什么工具来完成一个任务的时候,你几乎会毫无疑问地想到使用你最熟悉而喜爱的那个,这就是“当你手头拿的是一个锤子,任何东西看上去都像钉子”这句话的一个解释。原先的问题是使用一门技术使得能够性价比最高地实现要完成地产品,然而到你那里问题就悄悄地变成了“使用一门技术让我感觉最爽”,问题的所有其他需要综合考虑的因素都被选择性忽略掉了,所以如果你是一个语言技术 fans ,那么几乎毫无疑问你会成为一个糟糕的决策者。最近,在学习和研究的方法论上我已经听到不止一个人(参见《失败的逻辑》和这里——一位诺贝尔奖得主的忠告(引言如下))忠告我们,不要为了工具而工具,忘记了工具的目的是什么。

…我们前面提过的显微摄影专家,擅长于拍矽藻的照片,动物学家着迷于昆虫贝壳跟有美丽羽毛的鸟类。我们的爱书狂最高兴的事情,就是读最新书和专论,他认为这些东西很重要,而且很能够刺激头脑,可是别人没办法,找到另外一本同样的书。我们博学的模范,经过这种策略,让他的朋友惊奇,让他的朋友佩服…

…没有效率的科学家中,有一小类就是所谓的崇拜研究仪器的狂人,他们对金属的闪烁非常着迷,就像是夜鹰对它在镜里的反影着迷一样。他们非常仔细照顾所崇拜的东西,把仪器拭擦得雪亮,可以当镜子使用,而且把它摆饰在最崇高的地方,就好像在教堂里面的圣坛…

CodingHorror 的作者最近在博客里面跟着 Steve Yegge 同学宣称,如果有一件事情是他想教给程序员同学们的,那就是 Marketing 。无独有偶,有一次吃饭的时候鲍志云同学也提到: Marketing Sense 是很重要的。其实也就是不要总想着写牛代码,用牛语言技术,不要落入为技术而技术的怪圈,而是首先想明白做的事情有什么价值,先弄清做什么,为什么做,再去想怎么做,这样后面的功夫才花的有价值。

You won’t– you cannot— become a better programmer through sheer force of programming alone. You can only complement and enhance your existing programming skills by branching out. Learn about your users. Learn about the industry. Learn about your business.

当然,最后必须声明的是,不要矫枉过正,误会以上的观点,以上观点并不是说学生时代做的基本功是不需要的,一些非常基本的功夫(如计算机体系结构、数据结构和算法、两个主要流派(命令式和声明式)的编程语言都使用了哪些主要的编程范式、对主流语言的优缺点和适用场合的了解等等)是有必要掌握牢靠的,因为一方面我们并不是都能去做判断与决策,码农总是一个可靠的职业。另一方面对技术大方向的把握也是决策的基础知识,只是不要掉进无尽的技术漩涡,成为技术追星族。此外也许还有一个小小的好处就是如果被老板逼着用一门不熟悉的语言时不会很不痛快,因为真正重要的事情是你完成的产品,而不是用的语言

一个有趣的附录

我们的思维有很多很多的弱点,前文讲的其实就是这样的弱点,更多更系统一点总结可以看下这里,和这里。我一向认为,正确的思维方式,是一切高效学习的基础。比如参见如下2个例子,错误的思维方式得到的结论有大得多的可能性是谬误。

[1] 人总喜欢沿袭以往习得的经验,并通过类比来进行外推。我第一次在一个地铁终点站坐地铁的时候,看着从远方开来的地铁,我心生疑惑——“这车每节车厢都这么长,待会怎么调头呢(我心说没看到铁轨终点有一个大大的供调头的U形弯啊)?”,当车开始开的时候我终于意识到原来车是可以往两头方向开的。

[2] 人喜欢从关联当中寻找因果,有一次我我老婆去银行取款,到了 ATM 室的自动门口,我开玩笑地拿着手头的饭卡去刷了一下,然后——门居然开了。我顿时来了劲,立即得出一个结论:这个刷卡装置不安全,至少不是能够专门识别银联的卡的。我甚至飞快地泛化出了一个更具一般性的理论来解释这个现象:即可能所有带有磁性的卡都可以用来开门。老婆看我得意洋洋,就泼过来一盘冷水:不一定是你的卡刷开的啊,你不刷卡试试看。我不信,说怎么可能呢,心想我刷卡,门就开了,还有比这更明显的因果关系嘛。但出乎我意料的是,我走出门,这次没刷卡,门也开了——原来是感应门——原先这个 ATM 室的确是刷卡门,但后来改成了感应门,刷卡的那个装置只不过没拆掉残留在那里而已。

[3] 《失败的逻辑》里面从认知心理学的角度系统介绍了我们在复杂情况下的判断与决策是如何出错的,非常值得一读。

29 Comments

  1. keyeechen | | Reply

    其实我还停留在程序实现都感到痛苦不堪的阶段。写代码对我并不是一件快乐的事情,而是一件痛苦的事情。

  2. 黯淡蓝点_ | | Reply

    “如果结果发现这门语言并不何时来解决你手头的问题呢?”作者这句话的“何时”应为“合适”

  3. liu3617 | | Reply

    本来不想评价的,但是看到LZ如此贬低其它语言,我只能说,你根本就没认真看过其它语言,既然存在,肯定会有它存在的价值,不然早就淘汰了,还有就是这几年明显java要排在C++前面,LZ可以查一下编程语言排行榜,如果JAVA是渣的话,C++那不是连渣都不如???

  4. iezio | | Reply

    论思维时有老婆的重要性

  5. 清之一名 | | Reply

    我同时都赞同两种说法,也许WHY对于比较初级的人来说并非是做市场,而是能够明白自己的立场和位置,然后可以踏实得做好HOW的工作。WHY应该时刻存在脑中,只不过表现出来的形式依能力级别的不同而有所变化

  6. wero | | Reply

    总之,就是以结果为导向。

  7. 蒙面超人23 | | Reply

    解院长说:“最好的学习方法是小组合作学习。”刘哥说:“最佳的学习方法就是先广度优先遍历,然后择最合适的分支深入”两种学习方法各有千秋,要是能把两种学习方法合二为一,相信必能打遍天下无敌手。做什么事情都要讲究方法,可方法有很多种。当你习惯性的套用某一种方法时,你会发现,这种方法不见得解决不了问题,也不见得能把问题解决好。选择一种方法时,要考虑其背后的价值取向,不要为了方法而方法。

  8. nil | | Reply

    作者说的是对的,火车掉头的时候,只是把尾巴的那个火车头开起来而已,在终点有一个 Y 字型的的轨道,给火车掉头用

  9. 朱健强 | | Reply

    我认为“最佳的学习方法就是先广度优先遍历”这个方法有一个盲点:能“广度优先”是否依赖于曾“深度优先”呢?
    假设,你现在之所以能“广度”并且效果真的更好,会不会正因为你曾经“深度优先”过呢?正是之前“深度优先”那段经历令你能真正做到“广度优先”呢?

    这可能也是很多方法不得其法的原因,比如,这个比如也是假设的:

    面向对象,面向对象是由从面向过程中走过来的人搞出来的,正因为他们有面向过程的基础才能理解面向对象,但一开始就直奔OO的人就不怎么行了。

  10. Lydia | | Reply

    Hi, 有幸看到你的文章,突然觉得:最怕的即时那些不应该问WHY,应该赶紧明白HOW的人,非要弄明白WHY才肯HOW。这在日常团队里面,被称作是不服管、眼高手低的人。这句话有点像在说我。那我的问题出在哪里呢? 我就是很喜欢先考虑why的人,但是在工作中,我的领导经常批评我说:你考虑这个没有用…… 我应该怎么样纠正才好呢? 请高人建议一下 谢谢

  11. freedom | | Reply

    “以前我总是觉得一个公司里面,CEO/CTO 这样的角色是基本摆设,但我现在不这样想了。”

    其实很多时候不是下面的人看不见全局,而是上面的人不让下面的人看到全局。
    再说了,下面的人看到了全局往往又不能做些什么,并不是人人都能经得起失业的风险的;
    国人最擅长的是不是技术,而是内斗;
    所以说,CEO/CTO 这样的角色是不是摆设,并不一定,取决于那个人想干什么,可能是摆设,也有可能是实干家。

  12. ifthenelse | | Reply

    “当车开始开的时候我终于意识到原来车是可以往两头方向开的。”
    还是要掉头的吧?一条地铁是复线吧?

    • ifthenelse | |

      后来想了下,可能和道岔结合起来就不用掉头了。
      搜索后也是这么说的。“地铁动车组两头都有驾驶室,不需要调头。地铁都是复线,有上行线和下行线两条轨道,下行到终点后,下完客会往前开通过道岔转换到折返线上停下,司机到原来下行时尾部的驾驶室,开动地铁动车组通过道岔转到上行线,这样地铁就算是调过头了。 ”

  13. 涩小道 | | Reply

    兄台很多大作,都拜读了。唯独对这篇有点异议。
    正好我也是搞技术的,其实一个团队里面的人是各种各样的。需要不同的技能和经验。对于一个公司来说,最需要的SA是埋头钻研技术的(如果他很会玩市场,按现在北京的风气,很快就自己开公司去了,或者跳槽)
    对于很初级的程序员来说,你先不要知道WHY,先学会对基本的问题HOW。毕竟你觉得很困难的问题,其实是很基本的技能。

    按4像限区分,最怕的即时那些不应该问WHY,应该赶紧明白HOW的人,非要弄明白WHY才肯HOW。这在日常团队里面,被称作是不服管、眼高手低的人。因为WHY-市场要这么做,和HOW写这行代码之间,区别还是蛮大的。

    • 刘未鹏 | |

      我同意你的看法。

  14. nakeman | | Reply

    当车开始开的时候我终于意识到原来车是可以往两头方向开的。
    —————-
    不对不对,可以两头开,但实际操作不行吧。地铁几分钟一班,怎么可能双向的呢?不是吗?
    还有,我觉得此文有点文不对题,你没有讲多少方法论的东西,顶多是程序学习陷阱之类的。

    • long | |

      这个很重要吗!

  15. xdm | | Reply

    很赞同你的观点!

Leave a Reply

Your email address will not be published. Required fields are marked *