王辉的博客

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

  1. 服务精神:超越自我
  2. 服务精神:反馈循环

上次说服务精神的时候,选择了超越自我作为切入点。这次想从反馈循环的角度来谈谈服务精神。先撂个包袱,曾经有一个同事走了,我问他为什么,他说,因为他写的代码,很快就能部署到生产环境,用在客户手里。

什么是反馈循环

反馈循环,说白了就是有去有回,有来有往。你提供了一个服务,想知道服务的质量好不好,你就会问客户,然后根据客户的反馈提升服务的质量,然后把更好的服务,再次提供给客户,往往复复,越来越好。举个例子,你下了馆子,点了个鱼香肉丝,吃完,服务员问你,好吃吗?你说肉有点少。服务员记下来了,告诉给了大厨。第二天,你来了,又点了一个鱼香肉丝,真的发现肉多了,你很高兴,从此就常来吃饭。多亏了你这样的客户,饭馆的生意越做越好。

程序员,也一样,你写了个代码,但是不知道好不好,就想问问客户体验怎么样。然而,到这一步,往往是悲剧,发现没人可问,因为没人用。这就是我今天所想说的。让我们看看反馈循环的缺失所带来的危害,还有如何避免。

有用就是有人用

我出个上联:有用就是有人用。你对个下联。请仔细思考一分钟再往下看。

我很笨,只能想到这个下联:没用就是没人用。真不是逗你玩,什么叫有用?!如果都没人用,还有什么好说的。道理很简单,可忽略了它,会有很多麻烦。不少初创公司就特爱犯这个错误。有一天,你有个好点子,激动的不行,拉上一些朋友,热火朝天的干了起来,憧憬着融资上市时的样子,干了好久,终于要问世了,然而发现,你做了一个没用的东西,对!一个没人用的东西。浪费了时间,感情,和精力。

一个没有服务精神的人,首先想到的不是他的服务对象(很可能想的是成功了怎么办呀),自然,也就不会想着去问回馈。等想起来了,发现已经走得太远了,做了一个没人用的东西。因此,要避免浪费时间,感情和经历,就要首先培养服务精神,搭建反馈循环。

搭建反馈循环

其实,这并不是一个新的话题。很多前辈在不同的领域已经做了很多研究。比如精益创业里的”构建-测试-学习“循环;测试驱动里的”红-绿-重构“循环;还有敏捷开发里的客户融入开发团队的做法;持续交付,等等。

在所有方法的背后,我所体会到的精髓就是服务精神,而服务精神的精髓就是先想到别人。

拿了工资就不算浪费生命吗?

再说最后一点吧,也是我工作六年多了,才有的感触,和服务精神有关系。很多大公司,他们的商业模式往往是经过验证的,肯定是有客户的,否则也不会活那么久,招那么多人。然而作为一个程序员,我们能碰到的往往是大山的一角。写出的代码,从你的文档编辑器到最终用户的手里,往往需要很久,短了几个月,长了可能几年,因为你的项目就是交付不了。这种情况下,我们是得不到用户反馈的。

我之前的想法是,这关我鸟事?工资照常发,代码就算没白写。可培养了服务精神之后,我改变了想法,会有点忧伤。你想想,你用了三年时间写出来的代码,没人用。对,没人用!你不觉得,你这三年可以做一些更有意义的事情吗?

如果有一天我换工作了,我想我的原因,应该和开头那同事说的一样。

  1. 服务精神:超越自我
  2. 服务精神:反馈循环

一位同事最近要离职了,去更远的地方。从他身上,我看到了很多闪光点,其中最亮的就是服务精神。餐厅里,小二接待食客,我们称之服务。作为程序员的我们,用代码给人带来便利,也是服务。

有服务精神的人,更愿意去挑战自我,超越自我。

同样是开发一个API,服务精神缺失的程序员,文档不写,测试没有,每个月能按时领工资,就心满意足了。

有一定服务精神的程序员,他会想到使用API的人。他会写个文档解释API的设计意图和使用方法。他会写一些单元测试,保证代码的正确性。达到这个阶段的程序员,已经可以算得上优秀程序员了。你可以看看周围的同事,有多少人练到了这个段位。

有强烈服务精神的人。他们会想的更远,更周到。就像你去海底捞吃火锅,在你等人的时候,人家怕你无聊,给你提供水果瓜子。这个阶段的程序员,非常注重用户的感受。他们首先要确保API可用易用性。其次,他们还会想到API的性能,让用户在这里花费的时间越少越好。最后,他们还会考虑,万一服务出现问题的时候,如何帮助用户快速的找到问题的症结。

设计一个能用的API容易,但设计一个全面周到的API,难!有服务精神的人明白,用户的赞许和微笑才是他们快乐的源泉,所以他们并不会止步于可用就好,而是追求卓越的服务质量。这种动力会引领他们不断的挑战自我,超越自我。如果你身边有这样的同事,不管是你和他一起开发API,还是使用他的API,祝贺你,你中奖了。

2018年6月更新 已离职,再见,Murex!

三年前,我谈了一次Murex面试。之后,陆陆续续一直有朋友打听Murex。这次结合我的经历和见闻,从学习氛围的角度,再谈一下Murex。

为什么谈学习氛围?除了技术团队之外,我还自愿加入了Murex Dev Branding团队。Branding团队的目的,是为了提升Murex品牌,吸引优秀的软件工程师。想要吸引优秀的程序员,必须先了解程序员们的需求。为此,我们做了大量的需求调研。结果显示,程序员们最重要的需求,是学习!

DevDailies

爱学习,但没时间?Daillies帮助你在繁忙的工作中,找到学习的时间。

我的同事Jonathan,在Devoxx讲了他的DevDaillies的经历,Les Dailies: une nouvelle façon agile d’apprendre en entreprise。大致的做法是,公司内部的一个同事,选择一个他感兴趣的话题,每天十分钟,直接在办公室里给大家传授知识。你只需要转动一下椅子,就可以享受到一个精心准备的知识点。目前,同事们分享了Dailly C++, Dailly Java, Dailly Functional Programming。

Java,C++社区

每个小组所处的环境不同,使用一种编程语言的方式就会不一样。实时金融数据小组,使用Java,注重性能的优化。服务框架小组,更看重Java的扩展性,比如依赖注入。在不同的小组,不同的程序员之间分享编程语言的不同特性,是Java,C++社区的任务。除了分享,如果你有问题,也可以像以上社区寻求帮助。

Conference JavaOne, CppConf, Devoxx

Murex的程序员,每年有机会参加各种各样的技术会议,远到三番的JavaOne,近到骑车十分钟Le Palais de Congress的Devoxx。我本人上一年九月份公费参加了JavaOne,上周刚结束了Devoxx。这些会议,可以帮助程序员在正常的工作中,抽出身来,呼吸一下新鲜空气,扩大视野。

TekTalk

不仅Murex内部之间的人,可以通过社区互相帮助,互相分享知识。我们还会时不时的邀请一些外面的热情人士给我们做演讲。上一次的精益创业的演讲就是在TekTalk的情境下进行的。下图是RedHat的朋友给我们介绍Vert.x。

Meetup

为了方便程序员们在自己的公司就能参加Meetup,Murex会时不时为Meetup提供场地。

培训

Murex目前处在转型的重要关头,如何处理技术负债,如何在老代码的基础上继续创新是一大挑战。为此Murex组织一系列的和Craftmanship相关的培训,其中最重头的就是Working Effectively with Legacy Code的作者Michael Feathers的到来。

CodingDojo

喜欢算法和数据结构吗?每周四的中午12点到14点,算法爱好者相聚CodingDojo,训练我们解决问题的能力。从LeetCode,到CodeJam,到Project Euler,作为程序员,擦亮你的枪杆子!

看这张图,拿披萨盒子当演算纸,厉害了,我的兄弟!

CodinGame/HashCode Hub

竞技编程爱好者,在Murex也能找到战友。同事Manwe在Devoxx分享了他的AI战斗秘籍,每逢CodinGame的比赛,Murex都会组织一个CodinHub。

结语

如果你也有学习的需求,却苦恼周围没有学习的氛围,那你需要怎么办?请仔细想一想。
最后,希望这篇文章能更生动地给大家展现一下Murex。

这次利用业余时间参加了CodinGame的Ghost In the Cell人工智能游戏比赛,下面记录一下比赛中所积累到的经验。一是希望下次能做的更好,二来希望能给没有参加过比赛但是有浓厚兴趣的朋友,起到个抛砖引玉的作用。

赛果

  • 传奇者联盟
  • 总排名: 59/3508
  • 语言排名Scala: 2/50

游戏概述

比赛双方各拥有一个主基地和一定数量的兵力。除了主基地之外,地图上还有待征服的其他基地。每个基地都有一定的产能,一旦拥为己有,就可以生产新的兵力。征服一个基地,需要往基地上派遣足够的兵力,谁的兵力多谁胜。一旦占领基地之后,选手可以通过牺牲十个兵力来转化为基地的永久产能。此外,每个选手还拥有两个炸弹,可以投放到基地上,摧毁在目标基地上的兵力,并且阶段性暂停兵力生产。最后谁的兵力最多谁获胜。更多细则,请参考比赛主页

策略篇

之前的比赛(比如Coder Strike Back, Great Escape),可以利用优化算法(比如基因算法,Minimax算法)对整个游戏进行模拟,无须深入挖掘游戏策略,就可以找到非常高效的方案。

Ghost in the Cell这款游戏一大特点就是巨大的搜索空间(分支因子过大),导致我们无法通过全面的模拟来实现优化的解决方案。这种情况下,我们必须要利用人脑对问题深入分析,设计一个为这款游戏量身定做的策略。这种利用人脑分析引导程序搜索解决方案的方法,叫做启发式(heuristic)算法。启发式算法,不能保证最优的解决方案,但可以在相对短的时间内,找到符合一定条件的方案。

这种方法的效率非常依赖于人们对问题的理解,因此,选择了启发式算法,就要深挖游戏中的概念和规律。接下来是我为对游戏的理解。每次理解得到升级,我的排名都会直线上升。

部队机动性

一旦一个部队从基地上被派遣出去,我们便失去了对它的控制,直至它到达目的地。所以我们要限制在路上的部队,与其让它们长途跋涉,我们应该为它们找到中转基地。这样可以增加部队的机动性,如果在到达中途基地的时候,战况和预估的有差异,我们可以给它们下达新的命令,执行新的任务。

出乎意料的是,通过中转基地的路径,会比直达路径更快。就好像,某些中转基地之间有快车道一样。计算最优路径有现成的算法:Floyd Warshall algorithm。这里是我的scala implementation

距离和产能的天然效应

即便我们可以在部队的数量上胜过对方,我们也未必能征服对方。假如基地A和基地B每轮产生等量的部队,那么基地A必须在起始兵力上拥有一定的优势,才能攻陷基地B。

举例来说,基地A和基地B,初始各有2只部队,并且每轮都生产2个新部队(产能为2),基地A和基地B相距四步。如果基地A在第一轮输送2个部队到基地B,当它们到达目的地时,基地B将坐拥2+4*2=10支部队。因此基地A的2只部队并不能撼动基地B。
基地A必须派遣至少2+4*2+1=11部队才能拿下基地B。因此,基地A的兵力优势必须达到4(路程)*2(产能)=8以上才能发起有效的进攻。因此远征难,没有十足把握,不必发动攻击,而是要利用现有的部队去做更有效的任务,比如扩大产能。

一旦理解了距离和产能的效应,我们可以计算每个基地的时间线。简单来说,就是可以通过计算基地的产能和发往基地的部队,算出基地在每个时间点的占有者和所持有的兵力。这个时间点是我进行攻防策略设计的最重要的根据。

具体来说,对每个基地我都会计算它的20轮后投入产出比,最终的兵力/所需投入的兵力。

炸弹的释放和躲避

炸弹可以摧毁对方的部队,拖延对方的产能。用好它要选择好地点和时机。炸一个没有部队没有产能的基地,没有任何效果。等到自己部队都快要被消灭殆尽的时候再释放炸弹起不了任何作用。因此好的策略,是利用炸弹阻止对方的扩张,为自己的扩张争取时间。

很多人的都选择在比赛一开始,就释放炸弹,这样炸弹会经历一段相对很长的过程才能到达目的地,这个时长增加的炸弹打击的不确定性。我所选择的方案,是在地图的中心点释放炸弹。这样我可以缩短从炸弹释放到炸弹爆炸的时长,进而降低不确定性。

选择中心点当做炮台还有一大优势。值得注意的是,炮弹不能像部队那样,可以利用中转基地缩短两点之间的距离。因此先攻占中间点在发送炸弹可以缩短炸弹爆炸的绝对总时长。

方法篇

在处理一些复杂问题的时候,我们不可能事先就制定一个完美解决方案,因为有很多概念、规律会隐藏的很深。通常,我们要通过实践去学习,边实践边发现。下面介绍一下处理复杂问题的一些方法。

千里之行,始于脚下

边实践边学习的精髓,就是要实践。千里之行,始于脚下,不要怕步子迈的小,一定要走出去。所以,不要多犹豫,开始写代码吧。

这也是我学习离散优化课程中老师大力推荐的方法。先从贪婪算法开始,有助于帮助更深刻的理解问题,发现可以优化的地方,也可以从心里上得到阶段性的成就感。接着再设计更加稳定可靠的优化算法,循序渐进的解决问题。

观察

观察能力,是非常重要的学习能力,通过观察,你可以找到内在的规律,就比如远征难的概念。CodinGame里,你可以观看排行榜上高手的比拼,从他们哪里可以观察到许多有用的策略。

放松

如果你感觉到阻塞,停滞不前了。一定要学会放松,去散步,打球,放空,好的想法说不定在你洗澡的时候就冒出来了。

实战篇

完美的计划,精妙的策略,不执行,都是纸上谈兵。选择一款好的编程语言,好的开发环境,会节省你开发的时间,进而给你更多时间去实践和学习。

Scala语言

Scala把指令时编程和函数式编程结合在了一起,并且对Immutablility有特别好的支持,基于篇幅的限制,我接下来打算专门写一篇文章,介绍面向函数式编程在人工智能对战游戏中的应用。

CodinGame

鉴于CodinGame平台的特点,只能提交一个源文件的限制,我开源了我的CodinGame工具:CodinGame Scala Kit。欢迎大家使用。

结语

这段难忘的比赛经历,让我学到了很多东西。细想起来,主要是我真真正正的投入了进去,这就是竞技编程的魅力。

在人于人之间的相处中,换位思考有利于人们理解彼此的需求,进而促成共赢的局面。把换位思考用到软件的设计中,能够提升软件的质量。这是我在Michael Feathers的培训里,领悟到的最巧妙的一个思维方式。如果你对Michael的名字不熟悉,那么他写的Working Effectively with Legacy Code这本书,你可能听说过。

如何在软件的开发中做到换位思考?在回答这个问题之前,让我们看看换位思考为什么能提升软件质量。

不用不知道,一用吓一跳

你代码敲得很High,把属于你的那部分模块写完了。旁边的小组终于可以调用你的代码了,然而,不用不知道,一用吓一跳。没想到,调用你的代码,得给你准备好十个依赖关系,一个方法要传二十个参数。当然我是夸张了点,可软件的设计中经常会出现这样的问题。

当一个工程师设计代码的时候,如果仅仅站在自己的角度上考虑问题,那么他很难能体会到别人用他代码时的感受。不能从对方的角度出发考虑问题,往往会无法满足对方的需求,甚至损害对方的利益。

拿上面那个例子来说,如果我们能早早的站在对方的角度上思考,就不至于写出来一个需要二十多个参数的方法,换成是你,你也不愿意去用这样的代码。所以换位思考,理解对方的痛点,可以帮助软件设计者降低软件的复杂度,设计出别人方便用,也喜欢用的代码。

测试驱动开发

通过上面的分析,我们理解到了换位思考在软件设计中所能起到的重要作用。那么,怎么才能做到换位思考呢?测试驱动开发是一个极佳的工具,能够引导我们进行换位思考。

我之前对测试的理解,大部分是停留在正确性的层面,比如测试可以保证软件的正确性,重构的时候,测试可以保证已有的功能受到保护。其实,测试还有它的另外一个巨大的作用。它通过让开发者本人,先写测试调用自己的代码的方式,不知不觉的站在了被人的角度上去考虑问题。在让别人用自己的代码之前,先自己用用,看看会不会被吓一跳。这种换位思考写代码的方式,可以很即时,很明显的发现设计上的缺陷。

结语

测试驱动开发,不仅是一种工具,更是一种思维方式,它第一时间帮助工程师去为他人着想,从他人的角度,审视自己的代码。

这周看了Martin的演讲,讲的是高性能系统的设计。他劝我们一定要舍得时间和精力在数据结构上下功夫,因为数据结构会陪伴你的整个职业生涯,长久不息的回报你,是一个程序员最好的投资。

为什么数据结构会有这种收益?

每天都给你节省时间

掌握好数据结构能帮助我们节省时间,并且每天都能省,这是因为我们每天都要和他们打交道。这就像,你每天上班,必须得坐地铁,但地铁太绕,开车十分钟能到的路程,生生被饶了一个半小时,后来你终于学会了开车,开车上班,每天省了一个小时二十分钟。如果数据结构掌握的不好,Queue里边有什么方法,怎么用还得看文档,毫无疑问,这绕的太多,浪费了太多时间。

熟能生巧

熟能生巧,一个巧字,如果你有幸遇到了,我磐石般的相信,这将是你作为程序员中生命中最为闪亮的时刻。学习之道,引起我强烈共鸣的一本书,讲述了作者Josh如何成长为象棋和太极宗师。我想利用其中的一段小故事来说明这个巧字。

和很多普通象棋学习者不同,Josh学象棋的时候,不是从开局而是从尾局开始,他解释道,在棋盘上所剩无几的时候,才能体会到一个棋子所能爆发的最大能量。只有把每个棋子的用法都体会透彻之后,才能催生出妙招。在一次比赛中,当所有的人都认为要和棋的时候,Josh走出了连自己都无法解释的妙招,这也许就是传说中的无招胜有招。其实,这并不是一种偶然,而是他积累到了熟能生巧的阶段,当知识融会贯通的时候,巧妙的招数,想法便不知不觉由心而生了。

我相信,学习数据结构,也会有同样的功效。我时常会沉浸于一些程序员所创造出的新的数据结构或算法,不可自拔,感叹他们的聪明才智。可他们之所以能创造出巧妙的新结构,也是他们在基本的数据结构上投入了大量精力,不仅知道他们的实现原理,怎么用,而且还知道为什么要这么用。

可转移技能

数据结构是比较底层的知识,一般底层的知识都具有可转移的属性。举个例子说,你用Java编程语言,明白了TreeMap是一个平衡二叉树。那么恭喜你,你将会在所有的编程语言里碰到这个数据结构,这可以帮助你快速的学习新语言。

资源

修改历史

IO和交互

IO, 是一个应用和外界交互的手段。

应用,最关键的交互对象,是用户。通过IO,应用可以响应用户在屏幕上的按,滑,拉等操作,显示相应的内容。一款应用,必须通过和用户的交互,才能展现它的功能。如果按它点它都没反应,显而易见,这个应用没有存在的意义。

除了和用户的交互,应用时常需要和其他的外部系统配合,比如服务器,文件系统,数据库。交互过程中,应用会读取网络请求,发送网络答复,修改文件等等。这种交互可以让不同的软件系统相互协作,共同完成用户所期待的功能。

由此可见,对一个应用来讲,IO必不可少。

IO和测试

然而,从软件自动化测试的角度来看,IO不仅增加了测试的难度,而且降低了测试的质量。

从和用户交互的角度来看,首当其冲的难题,自动化测试的实现绝不可能依赖于用户的手动介入,否则就不能称之为自动化测试了。一种对策,是事先录制下来用户的操作,然后将其重现。还有一个办法,就是用编程的方式,模拟用户操作。仅仅处理用户输入这一部分,就给测试带来了不少麻烦。

除此之外,还有应用和其他系统的交互。如果一个前端应用使用了后台,那么测试前端就得把后台服务器跑起来。在某些情况下这根本无法完成,比如交互的对象是一个不受自己控制的服务器。即便应用所需的各个交互对象都可以被成功构建,文件或网络的写入和读出,都会大大增加测试的执行时间,进而降低测试的质量。

测试策略

在这种情况下,我们需要什么样的测试策略?这个问题的答案,将是我们测试方案最根本的指导思想。

你开发了一个应用,你最需要测的是什么?我建议你现在暂停五分钟,好好想想这个问题的答案。

你需要测的是按钮吗,是文字输入框吗,是数据库的读写吗,是HTTP的调用吗?不是,一方面它们应该都是非常成熟的模块,是基石,它们必须好用。另一方面,也是最重要的,它们不是你存在的理由!你需要测的,是能把你的应用和他人的区分开来的东西,专属于你的那一部分,换句话说,你需要测的是你的核心业务,核心竞争力。这就是我们的指导思想。

IO蒸馏

这恰恰是我想提出的IO蒸馏的概念,通过IO蒸馏,我们要把我们的核心竞争力给提炼出来,越纯越好,纯到所有的IO都被分离出来。一旦实现分离,我们就可以用一种模拟的假IO来替换难以控制的真IO,这样既能保证我们通过IO来测验应用的功能,又大大的降低测试的复杂度。

下图表示的是我们IO蒸馏的过程。

结语

对任何一款应用来讲,IO都扮演着至关重要要的作用,IO是应用体现它价值的舞台。然而,IO的自然属性对自动化测试带来了巨大的挑战。面对这个挑战,以测试核心竞争力为出发点的IO蒸馏,给我们带来了一套高效的测试方法。它把IO和应用的核心业务高度隔离开来,一方面使得应用的核心竞争力得以凸显,另一方面,让模拟假IO替换真IO成为可能,大大了提高了测试的效率。

延伸阅读

0%