2006-09-21

记录

又被上了一课。

可能是由于懒惰的原因,上学的时候很少记笔记,即使随潮流记了,也很少看。这个习惯到现在就是很少记电话记录、讨论记录、会议记录,总是用大脑记。偶尔,过一两个月会想不起来当初做的某个决定,比较糟糕。

今天又因为这个被“揍”了。客户改变了主意,让我们改方案,我们想找他们的负责人协调这件事,负责人说只有拿出以前的讨论结果记录才能去据“录”力争。搜遍了相关email也没有找到相关的记录,郁闷至极。而这个负责人会因为我们的某些改变而翻出某年某月某日的电话记录或email等等,质问我们当初那么说为什么现在又这么说,让人不得不服。

一定要养成良好习惯,工作中和人打交道一定要留下文字记录。
1)当面或通过电话讨论的问题,事后一定及时记录,并且通过email发送给对方确认,留存。
2)会议一定要有会议记录,包括各种讨论会,review meeting。

另外,平时有想法要及时记录下来,以免忘记或占用“内存”。

Team内应该有一个集中的记录blog或wiki,各成员不断添加,经常阅读。讨论结果留在大脑中只有特定人知道,放在公共的地方就可以大家共享,不至于经常出现:“不能怨我,那个会议(讨论)我没有参加,我不知道。”。

秋后算账得有账本,空口无凭!

2006-09-19

第六感? or 乌鸦嘴?

今天晚上从机房回来的时候,另外三个同事打了一辆车,我和另一个同事打一辆车,由于只有两个人,所以我们俩都坐在后排座上,而空出了平常是我专座的副驾驶座。

上车的一霎那,想起这个同事说的他一般不坐副驾驶座,因为如果出事故,那个座位最危险,顿时有种想法:如果今天出事故,我在后排应该没事,那多凑巧啊!

不知是巧合,还是我的意念起作用了,半路上真出事了,在过一个红绿灯的时候,车速较快,从十字路口的横向突然冒出了另一辆车,我们乘的车一下子就撞到那辆车的后侧面了,把那辆车撞到路边的人行横道上,幸好晚上路上没有人,否则又有人会遭受这“撞”来横祸。我们乘的车保险杠完全被撞掉了,那辆车的后部也瘪了。

之前我们在车内聊天,也没有注意红绿灯,更没有意识到会有突发事件,所以毫无准备,同事的鼻子被前面坐椅背撞了一下,看了看没有流血,问题不大,我的膝盖也撞到了椅背,下来走走啥事没有,只擦破了点皮。赶紧庆幸今天没有坐在副驾驶座上,要是在那个位置,这样的撞击,脑袋估计会撞到前面的挡风玻璃上,估计得开个“小花”,明天就得在脑门上“戴手表”了。

真得应验了上车时的怪想,典型的乌鸦嘴,抑或是神秘的第六感。Who knows?!

其实这也不是第一次和事故车打交道了,还有更惊险的一次。

很小的时候,怕路上的车,每次车一来就远远躲开。一次和大人们在路上走,他们推着东西,我就在路边走,远远地就看见后方(大概一公里外)一辆很大的拖拉机驶来,赶紧靠路边走。可是当拖拉机很接近的时候,可能是由于紧张,有点晕头转向,稀里糊涂,不知怎地就窜到路中间去了,由于拖拉机已离我很近,为了躲开我,司机狠打方向盘,拖拉机一下子就翻倒在路边的水沟里。幸亏这个司机,捡回了一条命:)。很幸运的是,那个司机也没有受伤。

心有余悸,同时为了运动一下,吃完饭我们就没有打车回来,花了半个小时走到宾馆:)

决定以后两个人打一辆车,把副驾驶空出来。长春的出租车司机开车实在太猛了 :(

2006-09-17

完美主义

昨天吃饭聊起人的性格类型,有人分析说我是完美主义者,我自己倒没有意识到,应该是他们看错了。毕业找工作前做过学校的一套性格测试,我只记得内向方面得分很高,是个超级内向的人,至于是否完美主义倒不记得了,应该得分不高,否则肯定能记住。

这么一说,倒想起两件事。
大概四五岁的时候,有件事情很困惑,就是湿毛巾怎么也拧不干,怎么拧都还是有水,于是要拧很多下,还是认为拧得不干。而看大人们一下就拧干挂起来了。这事很是烦恼了一阵子,某天和一个大我一岁的表姐一块洗脸,看她也和大人们一样拧一下子就挂起来了,于是便问她怎样才能拧干,她说她以前也有同样的烦恼,具体说怎么学会的忘了,记得最后说我再大一岁就会了。于是这个问题便放下了。随着慢慢长大,这个问题也不成为问题了,慢慢地也和其他人一样拧一下就算干了。也逐渐明白,没有人会因为毛巾还有点水就不停地拧,除了那个傻小子:( 如果说现在的我是完美主义者,那四五岁的我便是完美主义者中的完美主义者了。

本科的时候有一个学期利用周末在外面做专利申请文档翻译,找这个工作当然是为了挣钱,敢找这个工作是自认为自己的英语还不错,但是在翻译的过程中还是比较痛苦的,痛苦的不在于累,而在于老怀疑自己翻译的不正确,因为专利文档为了描述上的严密,一句话写的很长,往往一段就是一句,中间嵌套了好几个which、that,所以翻译起来很痛苦,很怕断错位置,把意思翻译错了。另外就是读英文容易理解,但是要翻译过来,用合适的词汇表达出来又困难了,总是不确信用词是否恰当。当然我翻译过后还有人校验,每次的结果都不错,没有啥问题。但自我怀疑的痛苦始终没有去除。

其实现在工作,偶尔还是有这个痛苦,不确信设计的构架是否考虑了所有情况,程序里的异常是否都捕捉完了,等等......好在有review机制,可以缓解一下这个压力与痛苦。

上面所说的这些烦恼有一个共同的特点,那就是这些事没有一个完成的标准,准确地说烦恼的我不知道完成的标准,因此总是怀疑自己是否完成,希望做得更好。极端一点,就变成完美主义了。自我怀疑、多疑也是他们判断我是完美主义的一个证据。其实,毛巾是否拧干了、用词是否准确、断句是否正确、设计是否考虑了所有情况、程序是否清除了所有bug等等都不可能有彻底的完成,所有的只是完成到什么程度。定义下这个完成的标准,判断是否完成就相对简单了,痛苦也会减轻。

有可能我没有意识到我潜意识里还是完美主义者,但是我认为完美主义是要不得的。尤其在软件开发中,永远做不到零bug,所能做到的就是控制bug的多少、影响程度等。软件开发的复杂性决定了bug的存在,需求的不断变化决定了enhancement的存在,资源(时间、人员、人员的技能)的有限决定了不可能清除完所有的bug、实现所有的功能。因此,完美主义者做软件是痛苦的,在产品设计的时候会因为需要抛弃很多功能而痛苦,在方案设计的时候会因为没有一个解决所有问题的完美方案而痛苦,在编码的时候会因为无穷无尽的exception、bug而痛苦,在准备发布的时候会因为系统还存在bug而痛苦。而现实是残酷的,从来就没有人开发过完美的软件。事实上也不需要完美的软件,需要的仅仅是好用的软件,在特点用户群中使用的软件,由于针对的是不同的用户群,软件的完成标准自然也不同(Five Worlds)。因此,软件开发不需要完美主义者。

不需要完美主义者,不代表我们可以没有目标,相反,需要的是明确的目标,为了达到目标,需要在各种因素中做平衡,即balance & trade off。目标明确了,才能在需要的时候迅速作决策,不至于等到“黄花菜都凉了”。比如写一个只自己用一次的工具,则可读性、可维护性这些重要指标是可以放弃的,用来换时间。

但是,作为一个合格的职业程序员,即使是做一个只自己用一次的工具,也不会在可读性、可维护性上差到哪儿去,因为这些已经成为习惯、成为品质,是很自然的事,而不是通过完美主义者的追求才能得到的。

因此,对单独的软件开发来说,不能追求完美。但是作为软件开发者,可以把完美作为一种持续的追求,以便沉积为内在的品质。就像手艺好的工匠一样,手艺是平时练成的,而完成一件作品时需要考虑这件作品的用途,再决定怎么做。

Review & Inspection

这周进展相当不顺利。让我上火的两件事:
1. Web service接口代码不可用,测试发现走不通,在公司根本没有测试过。一开始发布不出去,折腾一天终于可以发布出去,用模拟的客户端一调用发现代码代码根本走不下去,于是逐行单步调试修改,几乎每两三行就有错误,居然有的逻辑表都查错了,需求没有处理的地方也很多。费了一天的功夫调通了一个case,把实现的方法作为例子,发回北京修改,直到今天他们还在修改测试,通过这两天的交流,发现他们已经清楚问题所在并且解决了,下周一拿到这测一下应该没有大问题了。

2. 第三方开发Web portal页面逻辑性不够,对用户不够友好。逻辑层有些代码更是不堪入目。定购部分代码if...else...层层嵌套,没有140的智商休想快速读懂,这样的代码当然漏洞百出。今天把他们的人叫过来,花了半天时间进行页面检查、重构代码。当场演示如何重构代码,把那些垃圾代码逐步重构成可读代码,在重构过程中修改了逻辑上的很多bug。再次证实了:复杂的代码不是必须的、通常是有问题的。复杂的处理过程不代表一定要有复杂的代码实现,只有简单的代码结构才能正确地实现复杂的处理过程。在重构的过程中,结构一简化就能发现很多隐藏的bug,在复杂的结构下面,理解每行代码都很困难,不要说一眼就看见bug了。
有那些绝佳的反面代码,加上对这段关键代码的重视,相信放在课堂上这是一个精彩的重构讲解课,但不知他们能领悟多少,下次提交的代码是否能更接近可用代码一点?!

这两个问题几乎同时发现,倍感压力,一个是内部问题,另一个是合作方问题。对于合作方的问题原因这里先不说了,这里的“妖”事太多。对于内部的问题,细想下来,还是我的错误,一时疏漏,工作没有到位,导致大乱。

没有到位的工作现在看来是 review & inspection.

项目前期的review & inspection做的还是挺好的,后期由于各种压力太大,没有能够很好地坚持下去,导致失误。

由于采取的是整个team一起review,到最后代码越来越多,review占用的时间越来越长,逐渐负担不起,于是有的马虎,追求速度。另外,项目中由于人员有限,没有人专门写test case,代码过后的UT几乎没有做,直接feature test,test case就由代码的开发者进行写出,写出后我看了几个人的,都太简单,没有真正意义上的test case,于是发email发comments给全组,没有大家一起review,只是让大家重写。事后又由于其他事太忙,没有好好重新全部重新review,相信大家都能认真对待自己的工作。这样最后一关也丢失了。于是出现一点问题也就不算奇怪了。

学费就这样付了,总得学点东西,总结如下:

1. Review & Inspection 很重要。

2. 开发过程中需要Review 和 Inspection的有:requirement、HLD&LLD、Code、Test case、Schedule & Plan

3. 每种Review都要有开发流程的上下家,以保证没有遗漏需求、任务等。

4. Review 有三个目的:
     1)相互学习
     2)找问题
     3)加强交流,沟通
           team成员不仅要了解自己手中的一亩三分地,也要对项目有通盘理解,以便做到整体系统的可能,同时降低项目由于某些人离去或其他情况带来的risk.

5. Review人员尽量多元化,保证各种人关注各个方面,没有盲点。

6. 对于一个新项目组而言,代码的风格,组员的习惯各不相同,需要统一,可以利用Code Inspection。

     需要在code的第一周就进行code inspection,无论大家是否完成了多少完整功能的代码,只要有足够的代码进行共同学习即可。因此需要全体参加,当作讨论学习课,确定以后的统一风格标准。第二周需要继续进行,以检验代码是否按照上周inspection结构进行修改,巩固风格标准。由于全体参加,review全体代码,会花费相当多时间,造成效率低下,形成时间压力。

     如果两轮过后目标基本达到,则可进行部分人员参加的inspection,inspection的间隔也不一定是一周,每个人可以选择合适的时间进行,最好完成一定的完整功能代码后进行inspection。

7. 保证所有需要review的内容都进行review,而不能由于时间等压力就在review上省时间。

8. Review 可以先做homework,在meeting上讨论review的结果。如果时间比较紧张,也可以不homework,大家直接在代码的讲解过程中进行review & discussion。这种review需要参加者精力高度集中,作者要按照scenario进行walkthrough。

9. Review结果一定要指定人验证是否按照结构进行修改。

10. 要让参加者积极参与、贡献自己的智慧,可以分为普通reviewer,critical reviewer,critical reviewer必须给出review结果。

2006-09-12

Axis Server.userException NullPointerException

Exception in thread "main" AxisFault
faultCode: {
http://schemas.xmlsoap.org/soap/envelope/}Server.userException
faultSubcode:
faultString: java.lang.NullPointerException
faultActor:
faultNode:
faultDetail:

java.lang.NullPointerException
at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder
.java:260)
at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.
java:169)
at org.apache.axis.encoding.DeserializationContextImpl.endElement(Deseri
alizationContextImpl.java:1015)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source
)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknow
n Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContent
Dispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Un
known Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.DTDConfiguration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at javax.xml.parsers.SAXParser.parse(Unknown Source)
at org.apache.axis.encoding.DeserializationContextImpl.parse(Deserializa
tionContextImpl.java:242)
at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:538)
at org.apache.axis.Message.getSOAPEnvelope(Message.java:376)
at org.apache.axis.client.Call.invokeEngine(Call.java:2583)
at org.apache.axis.client.Call.invoke(Call.java:2553)
at org.apache.axis.client.Call.invoke(Call.java:2248)
at org.apache.axis.client.Call.invoke(Call.java:2171)
at org.apache.axis.client.Call.invoke(Call.java:1691)



Googled. Thousands of matched pages returned. Checked the previous pages, nothing helpful.

Finally, through debugging server side codes, found the root cause, it's just common Exception. Just because used third party framework or tools, it hides the low level exceptions, it makes debug painful :(

Google can really help in work, but it's not enough. It returned too many results and not accurate. It wasted a lot of time.

Where can I found such a search engine, it purely collect all kinds of problems and it's accurate solutions?

I think the knowledge search engine should have three features:
1) Understand questions.
    One question have many kinds of expressions. Different people have different expression method, the search engine should understand what people really wanna ask.

2) It should return as few results as possible.
   Too many results will drown people. It will waste too much time to find the useful answer.

3) It should accurate.
    The results should answer the questions, not just match the words of questions. It should be tested by many of users. That is, it have been proved to be real solution.

4) It should be dynamic.
    The results should be ordered in some extends by human beings. People find the useful answer will have an easy way to mark it. Then the result will have bigger weight in searched result order.


2006-09-09

清晰的期限

学了一招:让人帮忙或安排任务一定要有清晰的期限!!!

尤其是多部门合作的时候。
清晰的期限对双方都有好处,对请求方有明确的解决时间,对援助方有助于任务排序与时间安排。

2006-09-07

歧义的长春话

今天去小背篓吃饭,服务员在上饮料的时候随口问了一句:“谁是西瓜汁?”有人应答,看似并无特别之处。

想起了第一次来长春出差的时候,也是一起和同事来这吃饭,每个人点一个小火锅,有点蘑菇汤的,有点乌鸡汤的,有个同事点了一个甲鱼汤。在上锅的时候,服务员挨个问“先生是**吗?”,等到问:“先生是甲鱼吗?”时,点甲鱼汤的同事赶紧解释“不不,先生不是甲鱼,先生点的是甲鱼汤”。众皆哄然:)

2006-09-03

总有坏汤的鼠粪

正沉浸在刚才遇到大胡子司机的喜悦中,怀着对长春出租车司机的美好印象,乘坐一辆出租车返回,却不料遇上了“坏汤的鼠粪”。

车刚要到的时候,此司机看到前面一个女孩要打车的样子,便把车开到女孩面前,催促我们下车。我把钱给他,还没等找钱给我,又催我下车,我下车他把钱递给我,我要发票,却态度很恶劣地说没有,着急去拦那个女孩,那架势看着不是像去拉人,倒像要去驾车撞人复仇。我和他吵了几句,同行的人说长春这样的人多的是,劝我算了,想想也就几块钱的车票,便算了,把车门狠狠地一甩,让他走了。

人和人的差别咋这样大呢?!正反两面的基地都让我来回碰到了,物极必反?让位高兴之时也气愤一下。和气生财,为了争取一个顾客而在其面前和上个顾客吵架,能让人敢坐或乐坐他的车吗?!

在停车的过程中,女孩就走了,其实,我如果要打车,看车里如果有人要下,都会等到人下完再上,而不会因为看人没有下完就叫另一辆车。

所谓:命里有时终须有,命里无时莫强求!

2006-09-02

大胡子司机

前段时间网上流传一个MBA司机的故事(出租司机给我上的MBA课),读后颇感受教,没想到今天在长春遇到了同样令人惊叹的出租车司机。赶紧把经过写下来,与诸君共赏。

晚上从乐府酒店出来,上了停在酒店门口的一辆出租车,一上车便看到司机一尺多长的花白大胡子,光头,挺有个性。

车启动后问了我们到哪儿,便说:“大概12块的车费,我这是酒店门前的车,讲究的是诚信,不宰人,一般住这酒店都是外地人,对长春不熟悉,都会有被宰的担心,我一开始就告诉您多少钱,剩下路怎么走您就放心了。”觉得这个司机很特别,乘过这么多次出租车,除了黑车或不打表的,还没遇上一开始就主动告诉大概要多少车费的司机。

接着便热情地和我们聊天,问我们有没有注意到旁边的建筑的特别之处,我们回答没有,便给我们介绍说是省委,特别之处在于是伪满时期的关东军司令部。聊着聊着就聊到了脸上的大胡子。我们问留了多长时间,他说十年了,从开出租车那天开始留的。这时又买了个关子,让我们猜他留胡子的原因,并且说出了四个首先排除的答案:纪念某事、迷信、爱好(最后一个忘记了),让我们猜他留胡子的三个原因。想了一会儿,除了这四个原因,还真想不到还有什么原因要留这么长的胡子,于是让他公布答案,下面便是他说的大概内容:
第一,让人记住我,一般坐我车的人再坐我的车都能认出我,大胡子显眼,容易记,全长春没有出租车司机留这么长的胡子,全国也少有,所以一年以后您再坐我的车也会记得我拉过您。
第二,乘客绝大多数不会看车牌号码(长春的出租车票为手撕票,没有车牌号),万一丢了东西很难找到司机,我这大胡子在这,您一上车,不用记车号,等发现东西落下之后,只要往交通队打个电话说乘的是大胡子司机的车,他们就知道是我,给您联系方式,这么就解决问题了。我不是想要争取什么学雷锋的名声,只是这是基本的诚信问题。
第三,对你们也适用,做人要有自己的个性,不要管别人怎么说,俗话说自己脸上的嘴有时候都控制不了,怎能控制别人的呢?!要活出自己的个性,别人想怎么说就怎么说吧。你们要是去年(前年?)这时候坐我的车,还会看到我的长发。为啥把头发剃了呢?是因为那时候萨达姆被抓,人都说我太像被抓时的萨达姆了,有乘客还说要举报。那时候一等红绿灯,人都瞅我了,比大姑娘走在街上的回头率都高。
(边说边找出了驾驶证,上面的照片真得酷似萨达姆!)

太精彩了,如果上上海的那个司机很成功地应用了数字管理与对顾客的细分,这位长春大胡子司机就是成功地应用了品牌战略,树立了易记、诚信的品牌形象。让顾客主动记住他,而不是他来记住或分析每个顾客。

我们很自然地问了一个很俗的问题,每月能挣多少钱。大胡子很自豪地说:
4900左右,在长春这地方也算很好的了。主要是顾客认识我,相信我,北京、上海的乘客往往再来上海的时候都提前告诉我什么时候的飞机,我就去机场接。只要告诉我什么时候的飞机,我一定提前去等,我的原则:只有我等您的,没有让您等我的。等一出机场,一眼就能看见我,大胡子,不需要您来回寻找。
乐府门前有18台车,其他17台都印有名片,我就不印,得乘客自己记,我的号码也不好记,得记在手机上,并且我不告诉我姓什么,记姓名过不了几天就忘了,直接记大胡子,永远忘不了。
(我想起了很多司机递给我的名片,往往是往口袋一放就没有下文了)

等我们把号码记完,目的地也到了,计价器上不多不少,正好12块!

(以上为真事,相信有很多人还记得这个大胡子,他说的话也许记得不是十分准确,我尽了最大努力来回忆。为了保持连贯性,中间我们的插话也没有记录)