Category: 一家之言

Clean Coder的发现

今天收到了人民邮电出版社图灵出版公司的快递,是章显洲和我翻译的《程序员的职业素养》(Clean Coder)的样书,前段时间的努力终于见到了结果。说起来,我与这本书很有些机缘。

在Clean Coder之前,Uncle Bob曾写过Clean Code,这同样是一本好书,更凑巧的是,它的中文版(《代码整洁之道》)是我的挚友韩磊翻译的,此其一;2009年,在翻译完两本技术图书之后,因为感觉到翻译实在太累,我打算就此收手,最多做做译稿的审校,未料到Clean Coder审校到一半,自己竟阴差阳错地成了合译者,炒股炒成股东是悲剧,审校审成译者也算离奇,此其二;Clean Coder中的Clean,最后确定为我偏爱的“职业素养”,之前众人曾为这个词大伤脑筋,我曾提议为“拎得清的程序员”,被认为类似方言,不够普遍,最终想到的“职业素养”乃是某天妙手偶得,也非常满意,此其三。

不过,我更在乎的奇妙机缘是,因为由翻译这本书,有了意想不到的发现。

2009年,我已经翻译完《精通正则表达式》和《技术领导之路》,共计一百万字,深感翻译是苦差事,殚精竭虑,冥思苦想,所得(尤其是物质的)却往往非常有限;所以决定不再做翻译这类费力不讨好的事情,最多只帮忙做做审校。

然而“破例”翻译《程序员的职业素养》,本来以为又要暗无天日地苦干一段时间,其实不然。我最后发现,翻译并不全是苦差,而是个相当健康的爱好,而且更深刻感觉,生活应当具有健康的爱好。

所谓爱好,应当是有趣味、能引发兴趣的活动,你必然愿意为它去做一点牺牲。这样看来,“无聊”显然不算一种爱好,因为它没有趣味;无所事事打发时间,显然也不算爱好,因为这只是一种无奈的选择,打发时间并没有什么牺牲;相反,打牌、看电视却可以算一种爱好,因为喜欢打牌、看电视,你必须付出某些代价,比如工作开小差,比如偷懒不做家务。付出这些代价换来的,是兴趣或趣味的满足。

不过,打牌、看电视之类,并不能算健康的爱好。我以为,健康的爱好,不但可以提供持续的趣味,而且在追求趣味的过程中,是能够收获积累与提高的。譬如看电影,若只是一部部走马观花地看过去,多半算不上健康的爱好,但如果能花一些时间去研究和思考,电影看得越多,能看出来的门道就更多,感受也更多,这便可以算是健康的爱好了。同样的道理也适用于摄影——摄影本身可以算所以一种爱好,但不花时间去学习,“镜头后的那个头”永远不变,拍出来的永远是糖水片,其实算不得健康的爱好。

时隔三年,重操旧业来做翻译,我发现它天然就是健康的爱好:翻译本身是一种锻炼脑力的活动,并没有墨守不变的规矩,经常需要反复推敲琢磨,完成之后自然乐在其中;而且,随着翻译经验的增多,逐渐可以领略到不同语言之间的奇妙联系,会恍然大悟“噢,原来这个意思,用英语该这么表达,而汉语该那么表达”,之后阅读各种文本时又有了更深切的感受。

更重要的是,无论工作有多忙,生活有多么不顺心,晚间往电脑前一坐,气定神闲,专心致志地做上半小时到一小时,就可以忘记各种烦恼,看看自己终于又做了一点有意义的事情,心情也好了很多。这样看来,拥有了健康的爱好,就可以把生活分成几部分,从某一部分(健康的爱好)中提炼出乐观的心态,去面对不顺心的部分。如果人人都能找到或者培养出自己的健康的爱好,生活中的长吁短叹,应该是会大大减少的。

回想起来,我对翻译一直是有兴趣的,但之前为什么会把它当成苦差事,而很少体验“乐在其中”呢?我仔细思考这个问题,相比之前翻译《精通正则表达式》和《技术领导之路》时的起伏心情、痛苦煎熬,现在翻译《程序员的职业素养》时,可以更好地把握翻译的节奏,这便是最大的区别所在。

翻译《程序员的职业素养》时,我先评估了自己平均状态的翻译速度,再估算了每天大概可以抽出多少时间来翻译,与编辑确认计划之后,剩下的就是按部就班,每天“不以物喜、不以己悲”地执行计划,并为看到当天的工作结果而欣慰;最终,可以按时按质交出自己的译稿。翻译完成之后,我猛然发现,自己翻译这本书时,确定合理计划并持续执行的做法,与书里提到的职业素养,竟然有许多重合的地方,真是“万变不离其宗”,要做好做成一些事情,总有些共通的规律。

这件事也给了我足够的信心:随着岁数的增长,我们不再有年轻时天马行空的乐观想象,认为自己可以这样那样生活,可以不顾现实、不计成本地做这样那样的事情,而是变得现实起来;但是另一方面,没有期望、不值得争取的生活,其实是不值得过的。把握节奏感,就是清楚自己的节奏,可以预估出出来,按照可行的速度,到某个时候,自己大概能到达怎样的状态或程度。然后要做的,便是依靠毅力,持之以恒地坚持推进,直到达成自己设定的目标。

前几天在深圳,与一个毕业不久的小伙子谈起,没有目标的生活是不值得过的。以我的经验,如果你想象的生活里有健康的爱好,有节奏感的准确把握,无论具体形态如何,只要去做了,都可以算是幸福的生活。

享受职业素养

按:前段时间与章显洲共同参与了图灵出版公司Clean Coder中文版的翻译,这是我的译者序。

我在招聘时常问的一个问题是:在你过去的工作中,遭遇过哪些印象深刻的困难,最后是怎么解决的?依我的经验,简历写得再漂亮的人,如果这个问题答不好,大都可以直接忽略。为什么会有这种结论?因为我们需要招聘的不是“经历丰富”的人,而是“有职业素养的人”。你遇到的问题可能很容易也可能很难,但我看重的并不是问题的难度,而是解决问题的方式、步骤,以及反思的深度。拿恢复误删数据来说,这可能算非常简单的任务,我更感兴趣的是怎样分析问题,找了怎样的资料,采取了怎样的步骤,此后做了哪些措施来避免这种错误再次出现。在我看来,相比问题本身的难度,解决问题的方式和步骤,以及反思的深度,都体现出一个人的职业素养。

是的,上面我两次提到了“职业素养“。相比起“专业主义”、“职业化”等说法,我更喜欢用它来翻译Professionalism,因为素养强调的并不是天赋的神秘,也不是技艺的高深,而是持续积淀的结晶:一方面,它体现了能力和素质,另一方面,它又强调了持续的积累和养成。甚为职业开发人员,基本技能不够熟练,当然谈不上职业素养;但是能迅速地编写代码,却不关心代码背后的意义,不能迅速判断、解决程序运行中的各种问题,自信满满地为自己交付的程序承担责任,同样是与职业素养绝缘的——许多所谓的”高手“,其实正是缺乏职业素养的典型。

当然,这只是我对于“职业素养”的理解。由个体经验总结的“职业素养”,多有一鳞半爪的嫌疑,所以即便你觉得上面说的有道理,难免感觉只见树木,不见森林。其实真正的”职业素养“绝不限于上述几方面,而是要广阔得多,深刻得多。要想一窥技术人员“职业素养”的全貌,已经有很多现成的资料可以参考,Bob大叔的Clean Coder就是其中的佼佼者。

作为一本技术类书籍,Clean Coder中有相当的内容,是介绍纯技艺的方面,比如测试驱动开发等等,自认已经算“职业开发人员”的人,大概对此并不感冒(不过,我仍然建议你认真看看)。但其它的内容,绝对值得你感冒,比如:什么情况下应该对业务部门说“是”,说“是”意味着什么。如果你没有想过这些问题,或者没有明确的答案,不妨看看Bob大叔是怎么说的:

(说“是”时)你对自己将会做某件事做了清晰的事实陈述,而且还明确说明了完成期限(do with a clean end time)。那不是指别人,而是说的自己。你谈的是自己会去(will)做的一项行动(action),而且,你不是“可能(possibly)”去做,或是“可能做到(might get to it)”,而是“会(will)”做到。

就我所见,技术人员往往太容易说”是“,往往在没有明确目标和期限的情况下,就草率给出了确认的答复,而且并不将其视为自己的一种承诺。屡见不鲜的项目延期,有相当原因就是在这种不负责任的情况下说“是”所致。但是我们想想,似乎没有哪一个正经行业,会把不能完成任务的人视为“有职业素养的人”,软件行业也不能例外。

如果你觉得自己已经足够负责,懂得“是”背后所蕴含的意义和责任,也不过如此,我们不妨更进一步,看看关于说“否”。在第2章,Bob大叔介绍了两个项目搞砸的经过,他并没有像常见的所谓专家那样故作聪明地指出实施过程中出现了哪些问题,导致了失败,而是一针见血地指出:这两个项目之所以会搞砸,因为开发人员没有坚决抵制各种不专业的需求(比如一些无关紧要但成本巨大的需求),抵制各种不专业的行为(比如为了赶工期而降低对程序质量的要求),最终只好喝下自己酿出的苦酒。对此,Bob大叔总结道:

有时候,获取正确决策的唯一途径,便是勇敢无畏地说出“不”字……我们要明白,委屈专业原则以求全,并非问题的解决之道。舍弃这些原则,只会制造出更多的麻烦……

对我来说,这真是振聋发聩的号角。而且,这种思维,这种视角,其实是许多技术人员所不屑或者不愿面对的——最初我也这么认为,但尝试在工作中主动说了几次“不”之后,我逐渐发现:花三分的力气去抵制无理的需求,可以节省十分甚至二十分的开发时间;相反,自欺欺人地说服自己凑合接受了无理需求,往往会非常被动乃至无法脱身,到最后,项目就落得著名的IBM
OS/360操作系统的下场,越挣扎,巨兽就越深地陷入泥潭。

要学习这样的道理,当然也可以参加培训班,听取授课或者阅读讲义,但那未免太显正经而缺乏亲和力。Bob大叔的特别之处在于,他总是可以通过浅显易懂的故事,清晰而敏锐地揭示了问题的核心所在。其中许多故事正是他自己亲身经历的,阅读过程中常会会心一笑,因为遇到了开发人员都懂的妙趣,比如费尽全力也是徒劳,无法让其他人理解“编辑程序的程序”;笑过之后,又会认识到许多道理——无法让其他人理解“编辑程序的程序”并不是真正的原因,真正的原因是:“客户……对功能的设想,其实经不起电脑前真刀真枪的考验……问题在于,东西画在纸上与真正做出来,是不一样的。业务方看到真正的运行情况时就会意识到,自己想要的根本不是这样。一看到已经满足的需求,关于到底要什么,他们就会冒出更好的想法——通常并不是他们当时看到的样子……真正的解决办法,是约定共同认可的验收测试标准,并在开发过程中保持沟通”。至少就我的经验,这一点是说的非常之对的。我曾经尝试在与业务部门确定目标原型之后,要求对方指派一个负责人,在IT部坐班,负责协商、跟进整个开发流程,确认每一点修改,这样既保证最终结果符合业务部门的需求,又提高了开发人员的工作效率,综合来看,成效相当可观。

类似的例子还有很多,在阅读这本书时,我经常会惋惜:如果早一点读到这本书,或许我之前就不会犯这样那样的错误,就能更早更好地积累自己的职业素养,另一方面,能有妙趣横生的书讲述看似枯燥的“职业素养”,对读者来说,又是一种幸运。德国作家托玛斯·曼曾经津津乐道于“斜躺在沙发上整天阅读叔本华”的美妙感觉,那是因为叔本华的文笔优美、流畅,可以把哲学变为惬意的享受。作为同时读过叔本华和Bob大叔的人,我想说,斜躺在沙发上整天阅读Clean Coder,认识和了解开发人员的职业素养,同样是相当惬意的享受。

说说我理解的职业开发人员

应人民邮电出版社图灵公司的邀请,我有幸参与了Bob大叔所著Clean Coder(不是Clean Code)的翻译。

与前作Clean Code不同,这本书着重讲述的是开发人员的“职业素养”,也即职业开发人员应当如何做事。在阅读中,我时常会忍俊不禁,也会拍案叫绝,感叹Bob大叔把深刻的道理讲得这样通透。我虽然没有Bob大叔那样好的文笔,不过对“开发人员的职业素养”这个话题,也有很多话想说,索性分几个方面写下来。

学习

开发人员在工作之前,一般都已经经过大学阶段的专业学习。众所周知,大学的很多课程已经相当落后,教材也非常保守,所以我见过的好开发人员,不少都是自学成才。但是,这些问题并不能否认通过专业课程学习知识的意义,职业开发人员理解的“学习”,应当明确地区分知识、课程、教材:知识是重要的、稳定的,课程和教材是不那么重要的、变化的。

可以非常肯定地说,数据结构、编译原理、操作系统这类知识,是整个计算机世界的基石,是任何时候也不会过时的。即便毕业后不从事专门的科研,这类知识也会从你接触到的各种现象中体现出来。我在大学时基本抛弃了学校指定的课程和教材,但自己反复啃过影印版的《现代操作系统》,反复做过北大屈婉玲老师的三本《离散数学习题集》,后来在工作中受益匪浅——调优程序的性能,很可能需要理解调度、死锁、用户空间与系统空间等知识;重构复杂的布尔逻辑,很可能要依赖数理逻辑中的定律。如果当时没有反复的研习,没有深入理解这背后的原理,并且没有领悟到这些原理和各种现象之间的联系,遇到很多问题我很可能就会两眼抓瞎,充其量凭经验试错,无论如何,其效率远不及知识体系指导下的实践。

据我观察,大学生之所以对课程不感冒,除去学校和教师的原因,另一个因素是,几乎很少有教材能把看起来乏味的原理,和生活中遇到的问题讲清楚。学习图算法时,你是否想过“人、狼、羊、草过河”的问题可以直接由它来解决?学习内存管理时,你是否想过为什么Windows 95、Windows 98都那么容易蓝屏,到了Windows XP才有了长足的进步?我相信,如果能把原理与这类例子对应起来,你的理解就会深刻许多,印象也会深刻许多。不幸的是,这类“打通/联系”的工作,在国内教材基本是一片空白,国外教材也只有部分涉及。其结果就是,不少“有经验”的开发人员面对“32位机为什么只能支持4G内存”、“进程间通讯有哪几种方式,各有什么优劣”、“浮点数是怎么表示的,为什么是不准的”之类基本问题一脸茫然,不要小看了这些问题,不懂它们,你开发出来的程序只能凑合用,因为根子上就欠考虑,所以后期遇到问题要重构和调优,就会难比登天,最终搞到自己疲惫不堪。

对此,我的建议是:如果你现在还在学校,不妨仔细想清楚知识、课程、教材之间的关系,确定重要的知识,选择好的教材,自己安排自己的课程。如果你已经离开学校,而且感觉自己的基础并不牢靠,不妨从手头的工作开始,想想它用到了哪些原理,对应哪些知识,逐步、有针对性地补习。这其实并不难——我的朋友张东亮(@zhasm),之前几乎没有任何计算机基础知识,只是因为对正则表达式的爱好,找到了一份开发人员的工作,一年之后,他已经开始啃编译原理的书籍,而且确实学进去了。

以上说的主要是“专门”的学习,如果是工作之后的学习,会有很大的不同。

首先,工作之后的学习更多依靠自觉,没有几家公司会付出代价让员工像学生那样“学习”,所以更多时候,你只能花自己的时间、自己的金钱来学习。很多人一想到要花自己的时间,自己的金钱,心里就打了退堂鼓。要明确的是,公司没有老师那样强烈的责任培养员工“成长”,如果你找不到好的、薪水高的工作,很难责怪上一家公司没提供好的培训。所以,担心金钱和时间而放弃学习,最终的结果是自己的停滞,逐渐丧失竞争优势。相反,投入时间和金钱来学习,不但可以保持甚至扩大你的竞争优势,如果这种行为可以坚持、内化到生活中,也有助于保持健康、饱满的精神状态。

其次,工作以后的学习,需要努力摆脱工作环境的限制。我见过不少开发人员,因为工作限定在某个平台,某种语言,业余时间的学习便全部投入到这种平台、这种语言上,而没有思考自己是否合适做这些平台和语言,这些平台和语言是否处于没落期。在学校里,考分或许往往是唯一的度量,但在工作中,行将没落的语言和平台,你运用得再熟练,也于事无补。况且,过于专精于一门语言、一个平台,反而会限制你的思维和视野,影响迅速学习陌生知识的能力——要在短时间内熟悉陌生平台和语言的例子,在我们工作中并不少见,在整个IT业界中更是家常便饭。为了让它真的成为“便饭”,平时还是应当有意识地摆脱工作环境的限制,挑战自己的思维惯性。

责任

我曾经见过很多的简历,在“工作经历”里,项目描述写得天花乱坠,如何先进,如何复杂,采用了多少新技术,但是具体到个人责任,或者语焉不详,或者极其潦草。这样简历,体现的是责任感的缺失——对于自身责任没有明确的认知,也就没有足够的担当;这样的人,通常不用面试,就可以知道并不是合格的“职业开发人员”。

另一方面,我在面试时,经常会问两个问题,其中很重要的一个是:在你的工作经历中,收获最大或者印象最深的事件是什么。一般来说,如果能回答得有条理、有依据,大多可以判定为合格的职业开发人员。因为,有责任感的开发人员,大多不会把程序看成身外之物,更多地会把程序与自己的道德、声誉等等联系起来,甚至把程序看成自己的孩子;所以,必然会投入时间精力去总结、反思、完善、改进,就像照顾自己的孩子那样。其实,就我的经验看,真正的职业开发人员,不但能很好地回答这个问题,而且说起自己做过的事情,多有种充沛的自信感:XX项目是我做的,其特点是什么,我是如何如何做的,遇到什么问题,是如何解决的……涉及的技术不必很先进,开发的系统也不必很复杂,只要能够这么自信满满地一条条历数下来,你的职业素养就是无可厚非的。

业务

软件开发中,需求变化是无可避免的。虽然敏捷开发、极限编程宣称要“拥抱变化”,但真正做到拥抱变化,却是难上加难。原因在于:一方面,不少开发人员对变化本身就持怀疑甚至抵触态度;另一方面,许多需求完全是无规则、无理由地变化,不但造成极大的浪费,也严重影响开发人员的情绪。

这个问题非常普遍,也很严重。我思考了很久,发现比较合适的解决办法是进行角色的互换,尤其是开发方(包括开发人员),不能局限于“按照规程实现功能”的角色,而应当深入思考和理解业务。

不少开发人员最“理想”的工作环境就是:根本不关心自己的工作成果给谁用,怎么用,会产生什么结果,他们更喜欢这样的描述:什么类型的数据从哪里来,怎样处理之后,最后交给哪里。在架构清晰、流程完备的大公司里,或许你只需要安心填格子即可,但是拥有这样工作环境的开发人员,占总数的多少呢?更多的人面对的还是变化不定的需求,甚至连业务部门自己都不清楚自己要的是什么,这种情况下,只关心“数据从哪里来,怎样处理,交给哪里”之类的问题,无异于盲人骑瞎马,无异于挖坑埋葬自己。

相反,如果你清楚某个实现方案的缘由,知道它是基于何种应用场景,如何设计出来的,就可以在相当程度上把握它的价值和所需的工作量。如果更主动一些,可以和业务部门谈,这么做,将来会遇到什么问题,如果将来要改,哪些环节是可以改的,哪些环节是不能改的——如果你设身处地地为对方考虑,给出的建议一定比技术味道浓厚的“做不出来”更有说服力。如果做不到这么主动,你也可以预估,哪些业务是稳定不变的,哪些业务是一定会遇到问题需要改变的,然后可以合理分配工作量:对那些明显没什么前途的项目,可以适当保留资源,以免将来竹篮打水;对那些目前业务部门认为不重要,其实又相当有价值的项目,可以适当多投入精力,以免将来措手不及——要知道,业务部门提的“紧急”需求,多半不会考虑开发的工作量。

需要补充的是,做到上面这点,其实有相当的难度:一方面,你的技术功底必须足够扎实,在满足需求时,不仅仅是“模仿”现实,而应当知道这种现实,在数字世界里应当如何表达,如何重构,受到哪些条件和规则的限制(比如同一个抽象操作的不同实现,到底是选择Switch语句还是多态,其实是有章可循的,必须根据实际情况选择);另一方面,又要能跳开技术的局限,从更全面的视角理解、把握业务。不过,这是非常值得花功夫的——从某种意义上可以说,当前热门的“领域驱动开发(Domain Driven Development)”,说的大抵就是这回事。

时间

在软件开发中,时间绝对是一个非常重要的因素。在这方面,已经有无数的巨著,无数的案例,无数的先烈,但是时间,仍然是一个值得讨论的话题。

总的来说,人月是一个神话,我们不可能绝对精确地把握开发时间,但是这并不意味着,我们不能从某种程度上把握时间。我个人的经验是,计划是在现实参照下的不断调整和修正中逐渐准确的。最重要的,并不是确定远大的目标,然后限定多长时间必须完成;而是可以把大的项目拆分为不同的模块,把整个开发流程划分为不同的阶段。如果你的模块划分得足够细致,就可以以每个模块的工作量,相对准确地得知整个项目的耗时;如果你的流程划分得足够合理,就可以在各个阶段拿出看得见、用得着的结果,供业务方使用。这样,一方面避免了“到最后一起推出,却发现与业务方想象大相径庭”的尴尬;另一方面,在开发过程中,每个阶段结束,就可以提供一个阶段的生产力,作为开发方,在面对质疑时,有足够的资本和底气。

从个人方面,我注意到,职业开发人员还有另一个特点:就是可以相当精确地估计某个“小活”的工作量。以我自己和我的一些朋友为例,面对一些细致而且明确的需求,我们经常可以精确估计到工作量,时间精确到以半小时计。在紧密协作的“背靠背”编程中,我会说:现在是几点,所以我会在几点之前,给你提供怎样的功能,其行为是怎样的,接口是怎样的(行为和接口可以事先约定)。这样的自信,既要求对所需技术、会遇到难题的把握,也要求在头脑里对任务有完整清晰的模型。虽然难度不小,但能做到这一点,确实是职业素养的典型体现。

学到不会忘

博文视点的张春雨编辑告诉我,八次印刷的《精通正则表达式》已经全部售罄了, O’Reilly 与电子工业出版社续签了版权合同,准备重新上市,让我写一点东西。

该写什么好呢?

2007 年 《精通》上市时,我还在中关村,天气好的时候可以望见颐和园的佛香阁;而现在,窗外景色已经换成了珠江边的小蛮腰;对正则表达式的使用,也从随手拈来变得生疏——许多问题需要翻查《精通》,翻查自己写的《正则指引》。究其原因,与正则表达式直接相关的开发做得少了,古话说“勤则立,嬉则荒”,就是这个道理。

荒是荒了,毕竟还没荒废,虽然有很多细节需要查阅,但是我很清楚,某个问题能不能用正则表达式解决,该怎样解决。或者说,虽然手上生疏了,心里其实没有忘记,而这一切,归源都是之前死啃过《精通》的缘故。

在阅读《精通》之前,我已经查阅了网上的不少资料,对正则表达式有了基本了解,能像模像样地解决一些实际问题,可算“够用”了。这时候遇见《精通》这样“现实价值不那么大”的书,能静下心去阅读,其实带着点毕业不久的傻气,只是单纯想把它弄懂搞透。所以,遇到匹配原理这类看来没多少实用价值的知识,还会愿意花时间去揣摩、研习。回头想想,也正是因为当时有这种傻气,可算是意外的收获:工作中经常需要学习一些工具和原理,虽然当时也“学会”了,但不久就忘个精光;相比之下,正则表达式却是学到了“不会忘”的程度。更典型的例子是游泳,几乎人人都可以做到“一朝学会,终身不忘”。同样是“学会”,为什么差距这么大呢?

这个问题我想了很久,最后的答案是,“学会”的定义是不同的。

通常我们说“学会”了某项技术、某门语言,意思是“凑合能用”,或者说“可以对照文档( Google )解决问题”的程度——你用 Python 解决了一个问题,就说明你“学会”了Python ,哪管是步步 Google ,还是照抄现成的代码。而我们说“学会”了游泳,意思是可以在水里行动而不沉下去,更重要的是在游泳时不需要时刻背诵各种口诀:吸气—伸手—划水—蹬腿—抬头—呼气……,如果你在泳池里还要时时谨记这些口诀,是绝对谈不上“学会”的。

两者虽然都叫“学会”,其实相差迥异:第一种“学会”是“照猫画虎”,第二种“学会”是“融会贯通”,虽然都可以解决问题,但从第一种“学会”到达第二种“学会”,其实需要经历漫长的过程。而且,两种“学会”都能解决问题,所以在达到第二种“学会”的漫长过程中,你很可能感觉不到自己的进步,反而会困惑继续学习的意义乃至放弃——既然能对着文档操作,既然有现成的资料,为什么要去理解背后的原理呢。

对我来说,第二种“学会”的好处是显而易见的,最重要的一点就是不会忘记——学习的时间增长一倍,遗忘的难度将会增加十倍、二十倍甚至一百倍。这些年来,我见到了太多这样的例子:有人每次用到正则表达式都会抓狂,都要四处极力搜索、反复盲目尝试,花很长时间才能凑出、蒙对解决方案;另一方面,他们又不愿意花时间潜心学习《精通》这样的经典。因为反复遗忘,需要反复学习,最终浪费了大量的时间。

许多人不愿意专门花时间来学习正则表达式,是认为它属于奇技淫巧,并非工作必须。但这理由是不成立的:我们大部分人不是作家,但为了在需要的时候写得出文章,还是必须专门花时间来练习写作。而且,专门花时间来学习“非必要”的技能,以后往往能有意想不到的收获。我真切体会到并且懂得这个道理,恰好也是与《精通》的翻译有缘。

在翻译《精通》时,为了省却重新编排索引的麻烦,需要做到中英文版页页对应,于是我专门学习了侯捷老师写的《Word排版艺术》,并且亲手尝试了每个例子,记熟了有关的概念和术语,从此学会了格式和样式的角度定义文档,再不用为格式之类的问题烦恼。这些年来虽然用得并不 多,却没有忘记。去年写作《正则指引》时,我事先完整定义了各种格式、样式、引用等等,交稿时节省了自己和出版社大量的时间。

另一个例子仍然与正则表达式有关。去年,为了写作《正则指引》中Unicode的章节,我专门花了时间研读Unicode规范,虽然最终《指引》中没有列出学到的全部知识,但我对Unicode的理解已经不再限于“在程序中设定Unicode编码即可”。前几天,有位同事遇到Unicode字符Ä (U+00C4)无法打印的问题,于是我建议他使用A和¨ (U+0041和U+0308)的两个Unicode字符来表示(按照Unicode规范,两个字符可以“组合”成一个字符),果然解决了问题。这段经历再次证明,真的学会了,就真的不会忘。

亚里士多德曾说:所谓幸 福,就是尽情地施展我们掌握的技能,等待期望的结果。然而很多时候,我们以为自己可以解决,但是之前学过的技能已经遗忘,于是施展起来步履沉重、举步维艰,最后只能精疲力竭地等待结果,自然与幸福绝缘。相反,如果我们能把重要的技能都真正学会,学到不会忘的程度,自然可以接近幸福。如果你想收获自如驾驭 正则表达式的幸福,不妨从这本书开始吧。

关于程序员学英语的经验

按:本文为《程序员》杂志约稿,刊发于2012年3月号,名为《程序员学英语三部曲》,http://www.programmer.com.cn/10833/

总的来说,程序员可算是英语水平比较好的群体,因为在这个行业,英文资料是最全面、最及时,对英文资料的需求也最迫切的。因此,就我的观察,即便刚入门不久的程序员,面对陌生的问题,一般也能查阅英文文档,找到需要的信息。但是另一方面,我也发现,经常阅读英文文档的程序员,英语水平许多时候却不像“经常阅读英文”的样子。应《程序员》的编辑邀约,我在这里列几点自己的学习心得,供大家参考。

第一,既要看代码,也要读文档。

读文档只读代码,是很多程序员的习惯,也是导致程序员虽然读了很多英文资料,英文水平却没有相应提高的原因之一。以前曾在《程序员》上看到介绍阅读技术图书方法的文章,提出过“先代码后文字”的方法,也就是“先看代码,看不明白再看文字”。这种阅读法能极大提高阅读效率,但如果技术图书只看代码就足够,还要文字干什么呢?很多时候,代码只是冰山一角,代码背后的思维和逻辑才是真正的重头戏,只有写成文字才能解释,也只有阅读文字才能理解。

比如,两段代码都是 x = 5; 看起来没差别,但一段的文字说明是“x should be not more than five”,另一段的文字说明是“x should be no more than five”。不查词典,你能弄清楚两种说法的区别吗——前者是“x必须小于等于5”,后者是“x应当只有5”,意思不同,应用的方法与场合也不相同。

近年来,有越来越多的技术人员投身译介活动,这本来是一件好事,但如果阅读能力不过关,反而会造成更多的困扰。经常有希望翻译技术文档的程序员来找我讨论翻译问题,希望了解一些句子应该如何表达。一开始,我也认为这是中文表达的问题,但后来逐渐发现,其实更多的问题出在英文阅读上,所以我的回答经常是:你觉得作者这里说的是什么意思?引导对方把原文的意思逐步表达出来,其实这时候,真正的译文已经浮出水面了。

最近的例子来自这句话:But as with any web-based system, atom-based solutions trade latency for scalability, making atom often inappropriate for very low-latency notifications。这句话之所以难翻译,问题似乎在于,除去句子的主干,之前有一个But as…, 之后又有一个making…。然而我最后发现,对这个句子有疑问的程序员其实根本没搞懂trade…for…的用法(翻译为“基于atom的解决方案需要权衡延迟性和扩展性”),如果明白它是“牺牲xx换取xx”之后,整个句子就相当好理解,也非常容易翻译了:与所有基于web的系统一样,基于atom的解决方案为追求可扩展性,增大了延迟,所以atom往往并不合适用对延迟要求极低的提示。

要解决这个问题,首先要做的是改变“只看代码不看文字”的习惯,或者至少要做到“阅读文字之后,能明白它的意思与代码是一致的”;另一个有效的办法是通过阅读纯文字的英文资料来学习某些新的知识(比如关于深入原理的细致讲解),这个方法我推荐给许多朋友,非常有效。

第二,注意读音。

以前总听人说,中国人学了很多年英语,其实是哑巴英语。不知道现在的情况有多少改观,但就我所见,不少程序员虽然阅读了大量英文资料,也会加入英文的讨论组,也敢开口说,但是还会在读音上出现许多问题。这里说的“读音”,并不是字正腔圆的口音,而是一些术语的读音。

计算机科学的术语来源非常广泛。比如设计模式里,有一种模式叫Facade,许多人往往直接读作’fəkɑ:d,其实这个词来自法文,正确的读音其实是fə’sɑ:d;再比如伪代码的“伪”pseudo,正确的读音是’su:dəʊ,但是我很少遇到能把它读对的程序员,许多人干脆不会发这个音。

也许有人说,这些问题不重要,大家“将错就错”,约定俗成就好了,但事情没有这么简单。最近我参见某个技术聚会,有位嘉宾(技术高手)把框架名chameleon(变色龙)读成了’tʃəmiljən,而正确的读音是kə’miljən,因为没有文字资料,许多人听了半天才知道他说的是什么,一些不熟悉chameleon的听众更是到结束也没明白。中国人聚会尚且如此,如果有机会参加中外技术交流,读错造成的问题就更大了。

解决这个问题有一个非常好的办法,就是学习美国大学的公开课,耶鲁、斯坦福等学校的计算机系都放出了许多高质量的公开课,学习其中的一些精品课程,不但能夯实基础,还能顺带学会许多每天都要遇到,但不会或者读错的术语。比如我就从中学到,数据类型char的读音是kɑ:,而不是tʃɑ:(经多位读者指出,这个例子有误,kɑ:和tʃɑ:都是可以接受的)。

第三,锻炼英文表达。

如果你背过单词,大概听到过“被动单词”和“主动单词”的说法,前者是指“看到了能认出来”的单词,后者指“表达时能主动应用”的单词。就我的观察,许多程序员掌握的大多数英语,都属于“被动英语”——看到了能认识,但要表达同样的意思,未必说得出来。

平时这样似乎没有问题,可是到了查阅资料时,不会表达就成了大的障碍。相比中文技术资料世界中“无责任/不负责转贴”泛滥的情况,英文技术资料的质量要高得多,Google搜索资料的准确性也远高于百度;但是,要能够顺利应用英文资料,需要“主动”输入信息,描述问题,这时候“被动英语”就成了大问题。

我自己多次遇到过这样的情况:即便答案近在咫尺(输入正确的关键词,Google的第一条结果就是答案),但程序员就是一筹莫展——因为他不知道计算机的“嘟嘟”声是beep,不知道搜“多线程”资料应该用concurrency,也不知道“死机”是system halt,“黑屏”是blank screen,“(登录时)不停返回”是infinite loop……

要解决这个问题,最好的办法是在阅读资料时多用心,记住这些说法;另一方面,没事的时候多浏览stackoverflow之类的网站,不要因为问题与自己无关而忽略,多留心这些问题到底是什么,是如何表达的。这样,在自己遇到问题时,才能迅速找到可能的解决方案,节省时间。

有人说,以汉语为母语的程序员,学习英语已经是迫不得已,不但要会阅读,还要会读、会表达,真是难上加难。这种说法有一定道理,但是在目前并没有更好的解决方案,学会阅读、认准读音、锻炼表达,确实可以给自己带来好处。长远来看,要改变这种情况,需要中文技术圈的所有人员努力贡献高质量的资料(原创和翻译都可以),如果只是“无责任转贴”,既不亲自验证,也不整理格式,中文技术资料的整体质量只会持续恶化,反向逼迫更多的人把英语学好。

闲谈跨界

我的朋友韩磊曾说:跨界(工作)真是一件刺激好玩的事情。彼时我还无法体会这句话的真义,直到去年因缘际会自己也投身跨界,终于有机会切身体会到其中的滋味,所以有这篇文章。

其实在此之前,我一直混迹于互联网的圈子,自认为接触过一些真正的东西,比如大规模数据的抓取,海量数据的存储和处理,在线系统的维护……客服、文案等等工作也有涉及。我想,太阳底下没有新鲜事,跨界虽然是在不同的领域,做的事情大抵还是这些。但是真正投身实业,才发现事实远非自己想象的那样。

这方面突出的例子之一,就是虚拟世界和现实世界的交流。从某种方面来说,互联网或者纯软件开发,更像在理想的虚拟世界中进行,可以脱开现实的束缚,只关心核心的模型。“发一条确认的消息”是非常普通而且常用的操作,你用Java也好,C#也好,PHP也好,只要按照约定发送这条消息,结果都不会有多大差别;落实到现实世界中,情况要复杂许多:消息必须有实际的载体,有发送的动作,不同的载体和动作,又对应到不同的效率和准确率。举个现实的例子:许多客户端软件,通常要求输入条码识别产品,然后用键盘(鼠标)操作一系列对话框、选择框,执行后续的操作。这个流程看起来没什么问题,但是“扫描-敲键盘”的操作在对处理效率要求很高的情况下,却会成为瓶颈。对此,可行的解决办法是,将键盘/鼠标操作统一为几种消息,比如“是”、“否”、“取消”、“确认”,把这几种消息对应到特殊的条码,将这些条码打印出来,贴在墙上,并辅以不同的提示音。这样,需要输入“是”的时候,只需要用扫描枪扫墙上贴着“是”的条码,并确认听到提示音,就可以完成。大部分时候,操作员的手不用离开扫描枪,甚至不怎么用看屏幕,效率自然大大提高。深入学习了解每个操作、每种功能的具体发生情境,是从互联网/纯软件转到实业开发中,相当重要的一点。

另一方面,实业里有许多领域和环节,因为某些限制,一直没有建立完善的虚拟世界(概念模型),如果能够妥善运用技术突破这些限制,同样能够大大提高操作的效率和质量。比如在物流运输中,“封箱带”部分承载着“保证货物运输过程中不被调换”的职能,但其实“保证不被调换”并不只能依靠封箱带这种手段。如今可以通过先进的设备和完善的系统,记录追踪每一个环节中货物的状态,尤其是重量——进入某个环节时,重量是多少,离开某个环节时,重量又是多少,即便货物被拆分,总重也应当保持不变……前一段时间报道出来的iPhone手机在运输过程中被调包的案件,我注意到,盗贼精心制作了和真iPhone手机同样重量的模具,这样瞒过了各个环节,到最终开箱才被发现,看来是深入了解过整个流程的。

以上都是比对技术思维和现实思维,如果换一个角度,从互联网的视角来看企业开发,又会有新的感受。就我的经验,企业开发中,有两个方面可以大量借鉴互联网开发。

第一是借鉴互联网开发的松耦合、混搭(mashup)思维。传统的企业开发虽然也强调分层,但大多必须严格地按照某些框架和套路来进行,开发人员更主要的工作都是“填格子”,这样有两个弊端:选用的框架和套路并不一定合适,尤其不适合今天迅速变革发展的节奏,开发人员的思维和视野也比较受限,难以交付高质量的成果。而互联网开发虽然比较“乱套”,但天生就强调松耦合,强调“服务意识”,许多开发人员天生就知道调用网上的各种服务,受其影响,也愿意将自己的功能做成服务(而不是一段源代码或一个二进制程序)。在一个相对复杂的系统里,完善的文档说明固然不可缺少,但架构同样重要,各个功能是做成服务,还是做成源代码、二进制程序,很可能极大地影响未来的开发难度和开发成本。这方面,企业开发可以多向互联网开发取经,实际上,许多从互联网行业总结的经验,已经被证明完全可以用于企业开发,比如如今流行的REST模式,就不乏成功的企业实施案例。

第二是借鉴互联网的产品思维。传统的企业开发,更像功能的堆砌,功能的组织和引导都很成问题。我曾留意观察过一些大型企业的ERP系统,虽然看似强大,员工使用起来却叫苦不迭,突出问题界面无序,功能杂乱,数据密密麻麻,很难找到自己需要的信息,操作也很繁琐。而互联网开发早已进入“体验至上”的年代,用户习惯了“凭直觉”操作;在这表象之下,功能并不是少了,而是多了,并不是简单了,而是复杂了,只是以更直观、更清楚地方式呈现给用户,并且需要分析用户的行为,不断调整。两相对比,如果能在企业开发中多一些产品的思考,多一些用户体验的思考,往往会受到良好的效果。现身说法是,我们分析了几个月内,所有员工对系统中某个功能的调用操作行为,总结出若干特点,再加以优化,服务器的负载减轻了很多,员工的操作也简便了很多。在企业系统里,这类工作往往是大有可为,而且收效显著的。

太阳底下有没有新鲜事?

太阳底下到底有没有新鲜事?这是一个问题。如果有,为什么会有老话说“太阳底下没有新鲜事”;如果没有,我们每天分明又见到各种新奇的事情和问题。这到底是怎么回事呢?

不妨来看个具体的例子:同样是产生热量,我们可以给电热元件通电,也可以点天然气,还可以依靠摩擦生热,甚至还有很多我们意想不到的方式,每一种方式都其独特之处,这么看来,确实是总有新鲜事;但是从另一方面来看,这些方式可以归类为物理的、化学的等几大类,而其本质,无非是能量的转移,这么看来,说“太阳底下没有新鲜事”,又的确有道理。换句话说,“有没有新鲜事”取决于看问题的层面。通常,从具体细节来看,总是有新鲜事发生,但是分类归纳之后,往往并无新鲜可言。

不过,无可否认的是,面对新鲜事物,一句“太阳底下没有新鲜事”,即便是漫不经心说出来的,也非常有分量,充分体现出极具洞察力的专家的自信——我就知道会是这样;更进一步,不止自然科学领域,在社会科学领域,许多人也在寻找那些恒常不变的规律(或者也可以叫共性、要诀、招式),期望从此收获“太阳底下没有新鲜事”的自信。但是,他们真的能做到这一点吗?

且以近年来受极大追捧的经管类畅销图书为例,这些书之所以畅销,光看名字就已经可知道原因了:《追求卓越》、《基业常青》、《从优秀到卓越》……作者毫无例外地宣称运用科学方法分析得到了伟大(卓越)公司的经营秘诀,并且尽力将书写的生动有趣、引人入胜,旨在让读者闲庭信步间领略到“伟大的公司何以伟大”的秘诀——比如四大要素、六个步骤、八大法则等等,告诉读者成就伟大的公司并不是什么新鲜事。继而,依葫芦画瓢,也可以将自己的事业做到优秀,做到卓越……但是,就我的经验,这么做多半是缘木求鱼,充其量,可以算一厢情愿,只能体现人们对美好图景的幻象,具体原因在下面详述。

首先,经营公司要解决的问题,与自然科学的问题并不一样。自然科学解决的典型问题类似:生产某样产品需要多少原料,经过怎样的物理化学处理等等。但是公司经营要解决的典型问题则类似:根据有限的资源,到底是生产产品甲,还是产品乙,如果选择产品甲,需要按照什么次序,投入多少资源,有多大的市场,预期可以获得多少回报……每一个问题都是新鲜的,都需要具体分析。实际上,畅销的经管书籍也被迫承认这些方面没有多少成文的规律可循,它们大多以“需要有一个清楚而持续的战略”之类的说辞来敷衍。实际上,战略是否清楚,很可能是随着公司的经营而逐渐明晰的,而且很可能需要经常调整战略,所谓“清楚而持续的战略”,更多是事后的总结和包装,并不能在事前确认。

其次,公司经营的成败,很大程度上取决于与竞争对手的互动态势,而经管畅销书往往对此语焉不详,似乎更注重“内功”。实际上,与竞争对手的互动是非常考验脑力的:对这个对手,可能需要采取这种方式,对另一个对手,又需要采取另一种方式;更复杂的是,对手可能会根据你的应对做出调整,于是这一轮应对结束,下一轮应对开始……了解博弈论的人知道,随着双方的互动,形势的复杂程度可能呈指数级增长。同样的策略不可能适用于所有的对手,甚至对同一个对手,在不同的时机,也需要采取不同的策略。某个企业努力将生产效率提高了一倍,同时竞争对手提高了三倍,或者另辟蹊径抢走了市场,这样的例子是屡见不鲜的。

再次是执行,现在流行的说法是“执行至上”、“无缺陷执行”,但是经营过公司的人都知道,不同的人对“执行”的理解并不相同,同一个目标,不同方面的执行力度也是不同的。有时候需要“一鸣惊人”,确保产品在交付时没有任何缺陷,有时候又需要“先开火再瞄准”,一边运营一边改进。在这样复杂的局面下,“执行至上”或者“无缺陷执行”更像是动听的口号,却并不能产生现实的意义。

最后,卓越的公司,很少有始终“追求卓越”,从一而终地贯彻某些恒常不变法则的,它们的“卓越”,更像是一根链条——在每一个时期,在每一种环境下,采取了正确(或者说没太多错误)的策略。Intel在1985年决定全力进入周期较长但利润丰厚的微处理器领域,这是一个冒险的决定,可以肯定的是,当时的Intel并没有刻意追求“卓越”。这些环节前后相继,总的来看才能成就“卓越”,但由结果去倒推,断言甚至要求一直追求长期的“卓越”,则属于本末倒置了。

所以,我的观点是,在社会科学领域,尤其是在公司的经营管理上,很难说“太阳底下没有新鲜事”——每个阶段,每个方面,我们需要解决的问题都是新鲜的,正像之前在《收割庄稼V.S.砍伐大树》里面说的:解决这些问题并不像收割庄稼,而更像砍伐大树,而砍伐每一颗树时,都需要注意到它的形状和方向。如果一定要追求“没有新鲜事”的境界,找到什么恒常不变的法则,很可能只能“到此为止”:这些法则是每个阶段、每个方面、解决每个问题都必须遵循的一定步骤,却不是伟大公司的充分保证。当然,“成就伟大公司并没有什么恒常不变的法则”,倒真不是什么新鲜事。

骑高铁的马夫

我刚接触计算机的时候,总是为它强大的计算能力所折服,又有些不服气:计算机不就是靠“傻算”取胜嘛——简单重复操作,人类无论怎么锻炼,速度都不可能提升太多,芯片的处理能力却可以按照摩尔定律持续增长。换句话说,计算机算得比人快,“无它,手快尔”。

可是后来,我逐渐发现,用计算机解决许多问题,依靠的并不是“傻算”加上生搬硬套生活中的直观方法,而是“别开天地、自成一体”。我曾经遇到过一个每日运营数据分析的程序,需要10小时才能计算出结果(在当时的业务环境下,这速度完全不可接受),其思维就是生活中的直观方法。我花了一天时间来改进算法,最后只需要5分钟。这个例子我记忆犹新,它充分说明:强大的计算能力,并不能直接带来充沛的解决问题的能力;用计算机解决复杂问题,必须懂得计算机的“玩法”,理解计算机的逻辑,然后才谈得上妥善运用计算能力。

怎样才算懂得计算机的“玩法”,理解计算机的逻辑呢?可以举个排序的例子来说明。

排序,这几乎是我们每时每刻要遇到的问题,对普通人来说,排序就是把一堆东西按大小顺序组织起来;对应的,许多变成语言提供了现成的sort函数,对某些程序员来说,排序就是查找语言文档,调用这种函数即可。但是,事情真的这么简单吗?

为进行排序,需要定义一种关系R用来比较任意两个元素,以常见的小于(<)关系为例,a < b 可以表示为 a R b;现在要做的是,对于排序结果中的任意两个元素Xi, Xj,如果i < j(也就是说Xi在Xj之前),必然存在关系Xi R Xj。

这段话看起来繁杂,意义却很重要。常见的数值类型”天然“就可以进行小于计算,所以对不少程序员来说,a < b中的 <,和返回true或者false的布尔运算符没有区别。这样“凑合”确实可以解决简单问题,却无法处理复杂对象的排序,把人按照身高排序,把货物按照发送的远近排序,把向量按照夹角排序;因为这些时候,排序的对象并不是身高、距离、夹角的度数,而是人、货物、向量。还有些人,大概了解关系的概念,但没有考虑“对排序结果中的任意两个元素,关系都成立”,所以排序结果经常出现“局部有序,全局无序”的情况。

用计算机解决排序问题,必须首先定义“关系”的概念,它在编程语言中存在对应物——常见的数值类型往往会提供默认的排序关系,也可以由用户指定排序关系,比如Java中的Comparator类,Python中的cmp函数,都对应着关系的概念。

以上两点都了解清楚之后,就可以开始选择排序算法。请注意,排序算法与排序关系是彼此独立的——不同的排序关系,可以采用同样的排序算法;同样的排序关系,可以应用到不同的排序算法(这里体现的,是计算机科学中的“责任分隔”、“低耦合”的原则)。

学过算法的人大都记得,常用的排序算法有几种:插入排序、快速排序、归并排序等等。编程语言一般会提供通用的sort函数,确保排序的结果是正确的,所以是不是就不需要了解排序算法了呢?

一般来讲,如果排序的规模比较小(小于千),插入排序是足够快的,也足够简单,同时只需要O(1)的额外空间;如果排序的规模较大,那么选择快速排序比较合适,只是它需要O(log n)的额外空间;如果排序的规模更大,内排序已经不合适,则应当选择归并排序之类的外部排序(External Sorting)算法。

对应到实际开发中,经常会遇到各种场景,比如资源非常有限(典型的就是移动开发),或者运算量非常大(海量数据的处理),这些时候需要程序员理解各种排序算法之后的原理,如果不分青红皂白,只管随意抓一个sort函数来用,结果很可能不只是计算缓慢,而是根本无法实行。所以说,要用计算机高效地解决真正的问题,必须懂得计算机的“玩法”,理解计算机的逻辑。

亨利·福特曾说:“人们需要的是汽车,而不是更快的马”。相应的,汽车时代有汽车时代的规矩和逻辑,同样是赶路,已经不可能再用骑马的规矩和逻辑进行。计算机也是如此,我的经验是,“基本上,计算机可以无限延伸人的能力,前提是懂得计算机的逻辑”,如果在高速增长的计算能力面前还只能延续手工时代的直观方法和简单逻辑,充其量,也只是骑高铁的马夫。

钢的琴,冷的心

题记:今天看到吴晓波先生的《中国工人阶级的忧伤》,有感而发。对苏东改革有兴趣的读者,可以去看秦晖、金雁两位关于苏东改革的专著,逻辑严密,数据扎实,值得阅读。

2011年7月17日早上,我在上海正大广场星美影院看《钢的琴》;下午,父母在株洲千金电影院看《钢的琴》——若我在家,一般会三人同去观影,若不在,一般是异地协同。之后我问他们,感觉如何。答曰:片子不错。

《钢的琴》确实是近年来我感觉比较满意的国产电影,写实,有现实关怀,完整,构成周全抓人的故事;它不像那些豪华巨制让人感觉莫名其妙,又没有《疯狂的石头》系列那么戏谑超脱,也没有贾樟柯系列电影灰暗沉重的压抑,只是讲了一个现实、正常的故事:陈桂林是东北一家钢厂的下岗职工,老婆跟卖假药的人跑了,离婚判决带走女儿,女儿的态度是,谁给买钢琴就跟谁走,明知买不起钢琴的他,纠集昔日工友,铸造了一架钢的琴。

真正的钢琴有八千多个零件,所以故事稍有点超现实色彩,片中也确实有一些超现实的片段(比如雪夜下的独奏),但并不影响观众入戏,至少我感觉是如此。空旷的厂房,褪色的口号,零落的标牌,破败的生活区,都是记忆中准确的样子——株洲是交通枢纽,也是重工业集中的城市,我从小在这样的环境中成长,亲戚朋友也多分布于各个厂区,集中居住的生活区,各形各色的工人,厂办幼儿园、子弟小学、子弟中学……现在似乎只需要用“大而全”几个字就可以轻描淡写,也只需要喊喊“甩包袱”的口号,它们便一家又一家地破产、倒闭。

我记得那些朝夕相处的形象:有人是老中专生,说话做事认真得紧;有人兴致高雅,业余将我们一帮小孩子叫去他家教授书画;食堂的两位师傅,一位红案一位白案,手艺不错,却为“天师”的诨名而争风斗气……忽然间单位便破败了,大家如鸟兽散,自谋活路;爱好琴棋书画的那位,几无活路;稍好一点的,一把年纪还要当联防队员,忍着酷暑寒冬,赚生活费。

这种境遇,该怪谁呢?怪他们不是生活的强者?可是工人很长时间内分明一直是社会的“中坚力量”啊。怪他们没有早做准备?可是他们年轻的时候,谁敢设想工厂会一夜巨变,真敢的话,估计没有人会坦然接受“低收入高福利”“低收入高积累”政策。怪苏联带了坏头?可是苏联不说对工人,对农民都有良好的保障,工业发展之后主动“反哺”农民,完善了农村福利和医疗体制。

当然,最流行的说法是,这就是改革必要的成本,也是工人应该接受的命运?持这种论调的人很多,我倒觉得这堪称最荒谬的结论了——常有人对比中俄,可是不要忘了,东欧各国大都(比如波兰、捷克、匈牙利)度过了转轨的阵痛,维护了公正,也恢复了经济;俄罗斯若不是搞半截子休克疗法,也不应该有这么长期的衰退。在我看来,许多“必要成本论”者甚至连“休克疗法”有什么具体举措都不知道。作为官员,他们可以完全漠视公正,作为学者,他们只醉心于研究假问题,得出假结论。但是,他们把持了这个社会,把持了那么多人的命运。

而且我猜,这些人甚至把持了更多的内容——我与父母之间,除去对电影的印象比较一致,观影的环境也一致,放映厅里都只有寥寥落落的几个人。问问周围的人,对这段历史,没经历过的人不感兴趣,经历过的人,不知道也不太相信有这样的电影。没有禁播,没有禁言,但在生活的压力下,在虚假叙事的重复之下,在似是而非的论证下,记忆会“自然”地枯萎、褪色、消逝。

认真是一种能力

正则表达式的书稿终于结束了(暂定名《正则导引》),关上电脑细细思考,写完这三十万字,最深刻的感受是什么?我的回答是:后悔。

从2009年初我打算写这本书,到2011年年中正式写完,一共花费了两年多时间,期间虽然经历了许多变故,有些事情也无可避免,但其实不用花费这么长时间——最初是在周筠老师的鼓励下决定动笔,当时是有想法,没规划。之后在徐定翔编辑和卢鸫翔编辑的配合下,尝试写了几章,又遇到困难停住。多亏李笑来老师说“暂时解决不了的问题,先绕过去,接着写后来的部分”,于是零零散散地继续写下去。到2010年末潘爱民老师对我说:这个状态可不行,这么随意地写,就算写出来,也不会是好书,不如不要写了。明知潘老师是在用激将法,我还是心甘情愿地上了套,为争一口气,日复一日地写,半年左右,终于完成。

回头看这段时间,却感到一丝荒谬:其实真正“笔耕不辍”也只有半年的时间,之前两年在干什么呢,这两年里,我心理总惦念这这本书,因此放弃了太多其它,然而其实并没有真正做什么事情。况且,翻看日记,真正笔耕不辍的时间,其实并不到半年::3月12日,状态不好,早点休息…3月20日,要办的事情太多,又没有规划好,书稿明天再写…4月16日,做了好几张图,做到有排斥情绪,看电影… 这类情景比比皆是。套用《少有人走的路》中的分析方式,这类因果关系其实都经不起推敲。认真分析起来,状态不好时,专注做一些事情,是可以让自己状态慢慢好起来的;要办的事情太多,没有规划好,完全是因为“没有规划”而不是“意外太多”;做图做到有排斥情绪,是因为把几张图集中在一天做,其实分几天完成并没有问题……虽然每次写日记发现自己浪费了时间,都提醒自己要从明天开始认真,但往往坚持不了几天,又因为各种理由松懈下来。我想来想去,原因就在于,我总以为“认真”是一种态度,从不认真到认真,只要端正态度就可以,然而认真其实是一种能力,如果你不具备,即便想施展,也未必施展得出来。

关于“能力”,我曾与一个朋友深入讨论过,结论是:只有当你能施展出来的时候,才真正具有这种能力,否则其实就是没有——你的思维很犀利,却没见真正解决过什么问题,其实就是思维不犀利;你的英语很好,却没有真正用英语学习过其它知识或与外国人交流过,其实英语就是不好;你理解能力很强,但不去真正学一点东西,其实就是没什么理解能力。这个道理,在书稿的写作上也是这样:我曾经信心满满地认为自己做事态度足够认真,所以有能力坚持写完这样一本书,即便有所懈怠,也可以迅速回到正轨;然而真相却是,依靠着各位师友的指点,并且在很多次想要放弃的边缘挣扎着坚持下来,才真正完成。所以,“认真”其实不是种态度,而是种能力;这种能力,自己其实还比较欠缺。

后来认真读了点心理学,才明白惰性更类似某种本能,是“野火烧不尽”的,会从生活的各个角落顽固地冒出来,滋生繁衍,这或许是种本能,只能依靠日复一日地贯彻“认真”来扫除它;惰性侵入的区域越多,“认真”的能力就越弱,就越难做到“认真”。可惜的是,这个道理,大多数人未必明白,我(或许也可以说“我们”)时常安慰自己说,某某事情不是不去做,也不是不认真去做,只是觉得没有必要罢了,实质是把能力问题错认作态度问题。在一两件事情上这样做或许无可厚非,长此以往的结果却是“认真”能力退化,惰性张扬。对我自己来说,日常生活中欠缺较真的细节,造成的恶果在需要“认真”写作时集中爆发——意志薄弱,时常找些借口自我放松;心神散漫,写一段又要看看邮件,上上twitter;缺乏规划,不能合理划分任务,照顾自己的情绪……所以才会在完稿时后悔不迭:我本可以早些完成的。

另一方面,我也体会到,在值得认真的地方认真,培养“认真”这种能力,是可以辐射到其它许多方面,让自己受益的。比如为了确认Unicode编码的某个细节(其实这仅仅是书中的一个脚注),我认真阅读了Unicode编码的长篇文档,终于弄清了这个问题。这样做看似不值得,却在其它方面带来了意想不到的好处——比如,我再也不会厌烦为弄清细节而查证长篇文档了,在这之前却会因为厌烦而抵触。这种“厌烦”,看似是态度问题,说到底还是能力问题,没有能力把认真贯彻下去(至少,依照自己现有的英语水平和理解能力,没有足够“认真”到克服厌烦心理)。

平心而论,自己和许多人一样,希望过上“有意义的生活”,可究竟什么是“有意义”呢?太过随意的生活,“意义”总是要打折扣的,真正有价值的“意义”,总是是长期努力追求的结果。而认真的能力,就是这种长期努力追求的必要因素。

Update:感谢豆瓣的朋友Clyde.L向我推荐了这篇文章,Gumption Traps and Gumptionology,我也向大家推荐。老外的文章往往更讲究方法论,也更细致,他把Gumption Trap分了三类,并给出了对应的解决办法,值得一读。