王辉的博客

是什么让我对未知世界始终充满热情?

之前类似这种题目的文章很少去读,可最近这种想法经常闯进我的思考范围。可能是人马上要三十,到了要想这个问题的年龄了。和其他关于衰老的话题一样,谈起来难免有些淡淡的忧伤。可认真的琢磨之后,发现未雨绸缪是件好事,能看到忧伤背后的从容和坦然。

程序员也是人,所以有人的共性。十几二十的时候,很少会想到老。一是忙着学习考试,没时间。即便有点时间,搞搞对象,打打游戏,也不会轮到它。说白了,还是因为年轻,可塑性强,没什么好怕的。到了快三十的时候,就不一样了。工作之余,时间有了。人闲了,就容易琢磨事儿,还老爱自己吓自己。其次,三十岁的人,慢慢意识到老为何物,爷爷奶奶听不清看不明了甚至走了,爸爸妈妈白发多了。最后,想再折腾的时候,发现已经到了生育培养下一代的年龄,担的责任越来越重,不敢再冒险了。在这种情形下,如果还找到良方来从容应对年月带来的冲击,就会有危机感,害怕老无所用。

经验和精力

一个人随着年龄的增长,经验会更丰富,同时,精力也会减少。所以上了年纪后,要学会利用积累的经验去创造价值,而不是单纯的用精力去和年轻人拼。

明白了这一点,就应该注重经验的积累。如何积累呢?首先,得从自身找出路。做同样的一件事,有的人爱观察,爱思考,可以了解事物的本质和内在联系。也有的人,只注重事物的表面,不加深究,只获取看得见,摸得着的果实。日积月累,前者的经验当然会越来越多。其次,要借助外力。有的环境利于经验的摄取,而有的环境只会单纯的让人变老。一个充满挑战需要开拓创新的环境,和一个整日复制粘贴的环境,它们所蕴含的经验有天壤之别。

所以,作为程序员的我们,要勤于观察,善于思考,特别是在这个各种框架、语言、技术日新月异的年代。拿大数据这个炙手可热的新名词来讲,从之前的Hadoop和到之后的Spark框架,技术革新速度是令人无法想象的。然而,爱思考的程序员们会发现,有些东西,的确变了,而有的东西是没有变的。比如说基于图的数据结构和算法,分布式系统的扩展性和可靠性问题,以及系统性能的测试和优化方法,这些基础通用的懂事是没有变的。Spark的核心思想RDD是一个创新,不求甚解的人只会学习如何使用它,爱思考的人会摸清它的来龙去脉,知晓它的优势和局限。这样一来,牢固的基础和丰富的经验可以帮助很快的掌握新的技术。

谈完思考的重要性,再看看环境,上面说到的开拓创新的环境,是很好,可没有怎么办?答案是看你想不想要。环境是可以争取的,甚至可以自己创造的。

我在现在的公司,在同一个职位干了快有五年了。干了三年后,工作开始漫漫变得重复乏味起来。大部分的时间是我帮别人解决问题,而不是我去请教别人,输出大于输入。为了能创造一个更加开拓创新的环境,我曾经“威胁”老板说,如果再找不到具有挑战性的项目,我就要走人了。后来,在我的推动下,终于找到了让我满意的项目和环境,争取到了和公司架构师们一起做项目,向他们学习的机会。除此之外,我还去参加编程比赛,程序员见面会,主动做演讲。这些都是为了创造互相交流学习的环境。

说了这么多关于经验积累方面的见解,我想简单的谈一下精力。精力会随着人的衰老而减少,这是无法阻挡的自然进程。我们能做的,就是放慢它的脚步。唯一方案,就是运动。相信大家都明白生命在于运动的道理,我就不多说了。

潜心治学

上面说那么多关于经验的事,只是想从职业竞争的方面说明,老并不可怕,只要方法得当,经验可以转化为不可或缺的优势。加上运动的好了,可以老将出马,一个顶俩。

接下来,说说潜心治学的态度。很多人程序员做了两三年,就想着转行,转产品,转客户,转管理,就好像那些行业不需要养老一样。如果是对编程失去了热情,那么如此做便是无可厚非。但如果仍热情饱满,却因为担心程序员的未来,而转行,那么就没有必要了。

首先,从专家程序员的市场价值来说,他们能够解决别人解决不了的问题,他们能够看到别人看不到的深度。再某些难题面前,一个专家,不能被十个初学者换掉。因为这不是一个搬砖的过程,而是一个修行达没达到深度的问题。除此之外,潜心治学,可以满足人们对知识上的追求,有的快乐并不是金钱所能买得到的,刻苦钻研,攻克难关本身就是一个可以给人以巨大成就感的事情。

潜心治学,成为大家,并非易事,也正因为如此,它是一条可以让人实现巨大自我价值的道路。这样的人,从小的来讲,可以是某些项目的带头人,可以是专利的获得者,可以是书的作者,可以是一个讲师。往大的讲可以是图灵奖的获得者,可以是改变整个世界的人。

结语

哗哗啦啦用了好几天的时间讲了这么多,一是想告诉自己,要沉下心来,做自己喜欢做的事,并且享受这个过程。其次,是要记录下,处在三十岁的这个关口,我对程序员人生的看法。也分享给其他的程序员朋友,共勉。

我是热情洋溢的程序员,王辉。

延伸阅读

我们约好的是十二点见面,后来被推迟了,因为他要见客户。中午饭没能在一起吃,下午两点和他见着了,在分别了三年之后。

我们是老乡,都生在河南商丘。也是在法国求学时的校友,他读博士,我攻工程师。他自己动手作过馒头,从和面,发面,揉馒头,到下蒸锅,确实有一手。后来他学成回国了,带回去了一个媳妇,在法国认识的北京妞。走的时候,他把蒸锅留给了我。我学完直接留在法国工作了。

后来,从朋友圈里得知他创业了。我又找到了他。因为我也有一颗不安的心,即使工作三年之后的我已经过上了小康生活。

两点多见着他的时候,他的面庞依旧是那么的棱角分明。此刻的棱角分明,已不是青春小说里俊美男生式的棱角分明,而是经历过风雨雕琢后的分明。略高的颧骨,加上头上冒出的几撮白发,不禁让我想到沙漠中经受风蚀的石头,几分凉意侵入心中。他的穿着很朴素,要不是知道,我万万不会把他和电视里光鲜亮丽的CEO联想在一起。

他把我们带到了大厦后的星巴克咖啡馆里。我要了杯咖啡,他只点了一杯白水。三年,可以发生很多事情在一个人身上,也可以只简简单单的让他老三岁。不知不觉聊了三个小时,听完他创业的故事,我觉得他的白开水比咖啡浓。

后来,到了大厦的十五楼,他们的办公室,地方不大,四五十平米的样子,摆满了办公桌。进门的时候,大家纷纷把目光投向了我,我看到的是一群年轻并且富有朝气的面孔。一一握手后,我给他们讲了一些我工作中学到的知识和总结的经验。他们听的很认真,不少人拿着小本做着笔记。可以感受到他们学习的积极性。

讲完,大家所有人到楼下吃了西少爷,来自西安的快餐,豆花加肉夹馍。吃完饭,七点了,他们又陆陆续续回到了办公室,赶任务。

我又和他聊了许久,他讲到了他遇到的困难,高高的股份,低低的工资,家人对他的不解,商场的黑暗,技术上的困惑,招人留人的烦恼,管理上的摸索,国外国内的落差。但他很坚定,千难万苦,不曾动摇。

这就是我看到的一个创业者,一个可敬的创业者

工作了四年以后,我升职为高级软件工程师。这里的升职不是管理上的提升,其实我现在只需要管好我自己,而是对一个软件工程师成熟度的认可。一个高级工程师,较之一个刚毕业的新手而言,他应该能够独立的推进一个项目,从技术,到和其他组的交流协作,都能够独当一面。我最近遇到的一个难题,就是如何和产品经理有效的协作来保证项目的进度。后来学习到一个秘诀,就是要别人的承诺。

具体的故事,是这样发生的。我和项目经理,一起制定使用者剧本(user story),会议结束的时候,我们分了工。我的项目经理,要先在这些剧本里,加入验收测试,然后,我再预估它们的复杂度。换句话说,我的任务依赖着他的,他做不完,我就起不了步。

当天下午,大家很高心的散会了。第二天,我问他测试写明白了吗。他说他明天给我,我说好吧。可第三天,依旧没写好。我问他,他又说再等一天。三天过去了,剧本没写好,没测试,我没开工。

后来我的上司看到了,略有不爽,他说,怎么都第四天了,还没有开工。我当时想辩解,说不是我的错,因为我得等别人的活完了才能开始自己的,我是被别人耽误了。后来想辩解也没有意义,特别是这种推卸责任的辩解更没有意义。我的产品经理,当时也意识到自己确实没做好,在第四天,他写了测试。

后来,我找到了老板,高级高级工程师,问他我在这种情况下,应该怎么办?他给我讲了一下他作为新手时犯的错误,也给我说了一个解决办法。

在他是新手的时候,如果他依赖的人太慢,我老板就自己替他人把活做了。这样进度会有,但不是长久之计。首先,替他人把活做了,僭越了不说,到时候万一你做的并不是人家的本意,之后还得重头再来,特别是这种产品经理写验收测试的活,程序员一定要让他们从产品,从用户的角度来写测试,不能以程序员对产品的理解去写。其次,即便替人做的好,可自己的精力毕竟有限,不能作为长久的打算。

所以不可取,那么什么可取呢?回到我的经历中来,第一天,大家高兴散会的时候,不能就那么散了,而要问清楚,你什么时候能给我一个满意的答复,我们需要对方的一个承诺。承诺的远近并不是问题。远了,说明他有优先级更高的事情要做,要么,我不能干等,要么,我们把问题上传给决策者来重新讨论优先级。近了,更好。承诺的重要性在于明确各方的责任点和责任段,是为了更有效的交流,不至于把时间用在等待里。

技术负债,指的是为了实现客户的某个需求,而做出的一种短期见效快,长期有伤害的,在技术层面的决定。更通俗的讲,就是坐在代码面前,只顾现在,不管将来。代码满足了客户的需求,却写的很糟很乱,为以后进一步开发新功能,做了一个坑人的铺垫。

上面讲的是技术负债的大概意思,下面讲以一个亲身经历的故事,想说明的是,技术负债,如果你当时不还的话,一旦错过了那个村,以后就很难再找到那个店,想还就难了。

出于不能透露公司秘密的目的,我就只笼统的说一下,(其实和模拟金融产品的收益与亏损有关,;p)。那是一个规模挺大的开发,引入了不少新功能,用了基本一年的时间。出于模块复用的原则,也就是不再造车轮的原则,我们最大限度的重用了原有的模块。由于原有模块的使用情景和新功能的情景大有差别,在重用之前,我们先改了原有的,使它从不可以被重用改善到可以用。说实话,改的有点牵强。

新功能实现了之后,客户很开心。可我们,总感觉有些地方不太对,略加分析,发现,两个不同情景下的模块有不少共同之处,其实当初应该再往前走一步,把共同的部分隔离出来,写一个更加抽象的模块。把确实存在差异的部分放在不同的模块里。

我现在说的听起来容易,其实做抽象没那么显而易见。这要求你,从更深次的角度去分析两个不同的问题,融会贯通,找到它们的共同点。抽象,在编程的各种能力中,处于一个很靠前的地位。

问题发现了,可我们却作难了。为什么?因为新功能已经交付了,客户也很满意。你现在非得给客户说,能不能再给我两万块钱,让我好好抽象一下。哈哈,你不是在搞笑吧?给你钱让你抽象?!你抽不抽的好是一方面,万一把原有的东西也破坏了,你这不是让人家客户抽搐吗。

其实我们的客户不是那种不懂开发油盐不进的客户,他们也知道,现如今不抽,以后再开发新功能的时候可能就会更难。可是既然新功能好用,他们又有很多其他方面还没实现的想法要做,他们更倾向于立即着手于其他的项目,等以后有时间了,也有闲钱了,再让我们搞那些很抽象的工作。

可你仔细想想,谁会有时间,谁会有闲钱?所以技术负债,就像打尖住店一样,过了那个村,就没那个店了。

真的就没有解决方法了吗?我思来想去,结论是没有什么神奇的方法可以解决所有的问题。

你可能会说,留在那个村,别走,解决完问题,再走不就行了吗。我同意这个观点,留在那里不走可以,前提是你给客户预先说好,你需要多少时间,多少预算,才能把问题解决的好。可实际情况呢,很多潜在的问题,在项目初期很难被发现,很多问题都是在实践的过程中发现的。敏捷开发,有利于解决类似的问题。但是越是到后期,和技术负债相关的请求,就会越难被接受,因为的他们的投入产出比小,风险大。更糟糕的情况是,有一些东西,是需要事后的消化,后知后觉的,就像我上面遇到的情况一样。

设计重要,前期周全的设计更是重中之重。小的负债一定要在它的萌芽阶段就消灭掉,越早越容易说服别人。

谢谢您逗留阅读,我是热情洋溢的程序员,王辉。

前一段时间,人告诉我说,我在沟通方面的能力还可以进步。刚听说的时候,还伤感了一下,后来,明白了,就开始找一些可以提升这方面能力的书,
不知道是怎样顺水摸得鱼,糊里糊涂找到了一本很大气的书, How to Win Friends & Influence People, 只看了一下书评,就看到了其中的一条铁律,就是不要和人争吵。

仔细琢摸了一下,发现有的时候我还真是一个不错的辩手。有一次,一个做产品的哥们,问我能不能在某个模块里加一行日志,具体是什么给忘记了,我依然绝然的回绝了。当时的理由是,那条日志是一个底层模块应该写的日志,不能写在高层模块里。我们争得脸红脖子粗,他说,就一行日志,你加了得了,我怀着对软件设计的崇高敬仰,和对其他程序员高度负责的态度,据理力争,不依不饶。

上面只是一个例子,很多时候,只要是我认定的是对的事情,我很难被人说服,达不成一致的时候,结局往往是争吵。后来,我开始慢慢体会到不要和人争吵的真正意思所在。那就是聆听,敞开胸怀聆听。有自己的信仰是无可厚非的事情。但因为有信仰就没耐心听完别人的话,就只能说明,嫩,太嫩。首先,我信的东西未必是完完全全对的,争吵我就错过了一个纠正自己看法的机会。其次,别人的想法也未必就一定和我有冲突,毕竟我听到的可能只是一部分。最后,即便是我真的是对了,人错了,听完想明白后给他发个邮件拯救一下也为时未晚。

不信?试一试,世界仿佛更美了。

今天谈年终总结,说到我在哪些方面还可以再进步的时候,被告知,我要学会控制我的激情。后来我一直在思考这个问题,难道有激情不是一件好事吗?

我当时就有些困惑,不明白我们所谈的激情是什么。详细的问过之后,才发现,原来是有人认为我对技术的热情极高,高到有很多新鲜的事物,看了就想尝试。

我当时的第一反应,是极为委屈的。我纳闷,我什么时候,愣头愣脑的不假思索,不问一二的就埋头做产品了。我的经验中,一向我都是多方面考量之后,才选择一个解决方案,并且明确它局限的地方。
被解释道,说你这个缺点指的是你在实验性的项目中,出手太快,太靠激情引导。我没有完全被这个理由给说服,因为我觉得,既然是实验性的项目,就应该勇于尝试,敢于失败。

可除此之外,确实有的时候,做的不够成熟,出口太快,以致所说的话,条理不清,不能完全让人明白。这一点,即便他们不说,我还是有体会的。记得有一次,我看到了一个关于如何测延迟的文章,为之叫好。结果没有
把概念完全吃透,就给别人说,说这个有多好多好,导致被问到深层次问题的时候,不知如何作答,给人一种只求一知半解的印象。

从上面两方面的角度考虑,我认为,激情是必须得有的,有激情才能有效率,才能更快乐,才能发现新想法。但如果激情过于热烈,而导致身边的同事不舒服的时候,就应该有所节制。毕竟工作生活中,接触的是人,服务的也是人,
人与人之间的交流和关系要处理好。其次,很重要的就是,不要被新鲜的事物,迷惑了双眼,但凡新鲜的想法,都要在脑子里多过几遍,以一种批判的眼光去看待。话不能说太多,一旦开说的时候,一定要有深度,有信服力。

这个节制激情的建议,看似有些令人匪夷所思,但抱着一种一日三省吾身的态度,去看,确实能够找到,自己能进步的地方。

只要功夫深,铁杵磨成针。在钻了好几个晚上和周末后,我没曾放弃过的布朗运动终于动起来了。怀着喜悦的心情,
给大家伙讲讲我看到的,学到的关于优先队列这个数据结构,ES6编程,和Canvas动画的事。

演示

原理

这个系统模拟的是小球在一个有限的空间里完全弹性碰撞的情况。上面的演示中大小不一的小球一共500个。实现这个系统有三个核心模块:

  • 基础模块:两个小球之间的碰撞预测和碰撞后的响应解析
  • 统筹模块:管理所有小球之间的碰撞顺序,以及任意两个小球碰撞后对系统的更新
  • 动画模块:动态的描绘系统的状态

其中基础模块用到的是物理力学和矢量的知识,统筹模块靠的是优先级队列的数据结构,HTML Canvas帮助做动画。

动机

做这个程序,主要有两个企图。

  1. 想切身感受一下优先队列这个数据结构的美妙。这个想法是在看完了Coursera上的算法课之后有的,毕竟一下高效的模拟这么多小球的相互碰撞不是一件容易的事。
  2. 学习一下ES6这个编程语言。之前一直抗拒尝试ES6,是担心用到写不出优美的代码。后来看到最新的ES6,支持了模块,类,箭头函数等让大家期待已久的特性,手有点痒痒,决定试一下。

做完之后,别的先不提,单是把它真真切切,活生生的跑起来,就让我高兴了好几天,这便是程序员的最大的好处,创造东西有成就感。除此之外,收获还是蛮多的,下面稍微详细的侃侃从不同角度对ES6,和它周遭的生态系统的粗浅看法。

好的

到处都是Javascript

Javascript吸引我的最大的一个原因,就是我可以把它给部署到任何一台有浏览器的电脑,分享给成千上万的人。这就是Web的魅力。这个程序完全可以用Java,Scala或者其他语言写,
但想分享给其他人,其他人必须得先安装JDK,然后再下载我这个程序,再运行,才能看见点什么。
Web的话,点一个网址即可。并且我软件的升级对网友来说是完全透明的,他看到的永远是最新的版本。

模块化

在我看来,模块化的支持将把Javascript的有效打击范围再推向一个更广阔的阶段。因为模块化是构建复杂系统最重要的工具。从我这四五年做软件开发的经验来看,模块化有很重要的意义。

大事化小,小事化了

大事化小,是毫无疑问的,小事化了,就是在谈理想了。模块化的这个好处,是从我们人类认识世界的角度来出发的。一个人在认识复杂事物的时候,不可能一下就抓住它的全部,而是先从一些容易理解的比较基础的地方开始,然后一点一点积累出来,最后看到全貌。
举个简单地例子,让你算个
12*5+(24/6 + 16)/2.5 - 2
等于多少,你不可能一下就知道结果,而是先把这个复杂的算式,分解分解,弄成最基本的加减乘除,然后一步一步得到最后的结果。

做软件是同样地道理。把复杂的系统,分割成稍微简单地子模块,依次往下走,子模块做好了,大的系统才可能见得天日。这一点和任何开发语言都没有关系。甚至和编程本身都没有关系,因为它是我们认识,发现世界的基本方法。

你变你的,别影响到我就行

这是模块化带来的第二个非常关键的好处,隔离变化所造成的影响。在软件这个领域里,我们最喜欢的是变化,最害怕的也是变化。为什么爱得也是它,恨的也是它?

爱它使因为它带来的是效益,有变化说明客户有新的需求,需求越多,变化越多。恨它,是因为变化不好控制,一不小心,就造出了Bug,并且有时候都不知道,为什么改了东墙,却倒了西墙。

模块化所扮演的角色,就是隔离,一个模块内部的变化,一定要内部消化,不管你怎么消化,别影响到别的模块就好。就像我上面分的模块,动画模块和基础模块是隔离的,小球怎么撞那是基础模块的事,不管怎么撞,动画模块画球的方式都不会受到影响。

拿来用用

吃别人嚼过的馍馍没味,但用别人已经做好的模块就很爽,因为这能给你节省时间和精力,让你专注于别人没有而你独有的东西。这是我认识到的模块化,激活的第三个非常关键的特点。
我的这个模块里,直接用到的别人做好的模块是优先级队列这个数据结构,在es-collesions里。我用得没有预期中的那么顺利,因为它由Bug,就像所有的拿来主义一样,要持有敢怀疑的态度。我是最后才怀疑的它,最后证明Bug就在他那里,我给他修了

生态系统

这里我所谓的生态系统,是指的在Javascript这个语言周围的一些辅助开发的工具。

Atom

Atom是Github的一款文档编辑器,用Javascript/Coffeescript写的,但它早已超越了文档编辑器的范畴。琳琅满目的插件,多种多样的功能
完全可以使它成为一个开发的整合环境,你所需要的代码补齐,代码高亮,搜索替换,git,vim等等很多工具都可以在里边找到相应地插件。唯一不足的是,
缺少调试功能。

Webpack

我查得第一个工具就是构建工具,稍微一查,真是有点被吓到。构建工具太多了,虽说百家齐鸣是好事,但多了也麻烦,这就牵扯到标准方面的事情。大概看了一下,最老牌的是Grunt,相当于Java里地Maven,推崇的是Convention over Configuration,我很喜欢这种方式
,大家都用同样地东西,学了一样工具,就到处都可以用。比较新的有Gulp,我说它比较像Java里的Gradle和SBT,Code as Configuration,配置文件本身也是代码,它省了不少配置的复杂度,也很有吸引力。

可最终我选择了,配置最简单,支持ES6最快捷,注重模块化的Webpack。几行配置文件就搞定了ES6的支持,并且自动生成一个Bundle整合文件,HTML只需要导入这个文件即可。很快就可以上手。它最有吸引力的是它关于Code Splitting的关注,可我后来意识到这里所说的
Code Splitting更像是Java 9里谈到的自动加载独立模块的概念,在我的项目里没用到。稍微细说一点,就是你有很多模块,当一个用户用你的应用的时候,未必一下子需要把这些模块都加载进去,提取给他有用的那些部分模块就好,简单地说是为了减少代码的下载量。

测试驱动

测试驱动,在使用像Javascript这样的松类型的开发语言的时候,所扮演的角色就更重要了。松类型的一个弱点就是不能在编译阶段找错,所以我们犯错误的概率就更大一点,所以早测试就尤为的重要了。
Webpack里有测试的Plugin,但是都是基于浏览器的。由于我的基础模块,和统筹模块都不操作DOM,完全可以脱离浏览器独立的测,所以我就选用了Mocha的单元测试,和Babel编译器相结合,单元测试非常快。试了一下Karma,是和浏览器结合的,太慢了。

Animation with Canvas

最后,说一说动画模块,一个简单地函数window.requestAnimationFrame,就天衣无缝的把网友计算机的屏幕刷新机制和代码联系了起来,
这真的让人叹服。我觉得这是Web的另一大魅力,几乎所有我们能想象到的应用都可以用Web的技术来实现,WebSQL, WebSocket, WebComponent…

可改进的

松类型(loose typing)

松类型是有好处,因为它给了你更多地灵活性。我在基础模块了就用到了这一点。可以和小球碰撞的物体有两种,一个是小球和小球撞,另外一个就是墙和小球撞。我在球和墙的代码里,都写了一个撞小球的方法,它们不用继承同一个父类,我可以直接把他们放到同一个集合里,
然后调用它们共同的方法和小球撞。这在Java,Scala或者Haskell这些强类型的语言里是不可能实现的。

但它也有它的缺点,首当其冲的是,松类型不利于在编译阶段发现错误,这个弱点可以用单元测试来弥补。但最让我头疼的是,一个函数所传达的信息是不完整地,它只告诉了你它接受什么参数,而不告诉你要返回什么。这对代码的可读性是一个非常大得影响。我想一个可能的
改善的方法,是把函数的名字,起的更详细完整一些,在函数的名字里,告诉别人它要返回的是什么。

下一步

Webstorm IDE

Intellij在Java中的体现是有目共睹的,相信WebStorm也可以做的很好。Webstorm相对于Atom,对我的吸引力体现在它对调试的支持上,
也许我应该写更多的单元测试,彻底地省去调试的需求。

JSHint

我看了两个Douglas Crockford的演讲,一个是比较老一点的JavaScript: The Good Parts,
另一个是新一点的The Better Parts,他都提到了如何用JSHint帮助我
们避免Javascript里地一些陷阱,只用它好的部分。

尾部递归

另外一个我没有提到的ES6的新功能便是对尾部递归的优化,这么一来,我就完完全全没有必要在代码里使用循环了。

0%