10年前家中丢的东西没有结案离婚证丢了还能结婚吗查吗?

博客访问: 39414
博文数量: 40
注册时间:
认证徽章:
从事银行核心系统设计开发的程序猿
ITPUB论坛APP
ITPUB论坛APP
APP发帖 享双倍积分
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 信息化
不久前,才把一个做了快要两年的案子结束掉。有个年轻的同事就问我:『奇怪,照理说结案应该令人觉得很兴奋,可是为什么我一点高兴的感觉都没有?』
我们从小就听了很多『从此以后,王子与公主就幸福快乐的生活在一起』的故事。很多人可能就认为,专案应该像是做爱一样,在末期努力冲刺之后,就会达到最后的高潮,接着就在激情中,完成一个完美的句点。所以到了那一刻,应该会让大家心神俱醉,精神亢奋,快美难言。所以常常都有人说,要召开盛大的party来庆祝专案的结案。
有这种期待的人,去看交响乐团演奏时,大概也会期待乐曲会遵照这样的模式吧。每首乐曲一定要在最后安排最大的高潮。在接近乐曲最后的高潮时,整个节奏就会莫名其妙的加快,声音会越来越大,跟男女交欢差不多。等到乐曲尾声时,所有乐器就会发出轰然一声,然后就突然万籟俱寂,只剩下首席小提琴微弱地用他无力的手拉了几个微弱的抖音,整个演奏就此溘然而逝。还没提他把琴弓抖一抖想甩去他的汗水的样子,与男性曳甲收兵时的景象有多神似哩。
我听的古典乐曲很少,不知道有没有这种奇怪的音乐,不过我做过的专案中,还没有一个是在激昂澎湃的音乐中结束的。有经验的人告诉我们,软体专案的结案跟做爱不同,反倒是跟结婚蛮像的。结过婚的人都知道,筹备结婚本身就是对两人生活的第一个考验。经过漫长的筹画,把很多琐碎的事情都处理好,拍婚纱照,选礼服,订场地,选菜单,选戒指首饰,寄喜帖,送喜饼,遵照双方家长认可的礼仪迎娶,行礼如仪,(都已经什么年代了,还要特地买把纸扇掉下来?)婚宴敬酒,等着被闹洞房...一直到把闹场的人们从新房赶走以后,还得拖着疲惫的身躯,去面临新婚之夜与蜜月旅行。
很多小说都会描写,主角在完婚之后,新人交杯对看,眼中深情无限,彼此沉浸在绵绵密密,无边的幸福中。或许你早就知道了,小说跟实际的生活还是有段差距的。我只能说,如果你没被灌醉的话,有不少人的标准感觉是:『呼!终于结束了。好累啊!先好好睡一觉吧。啥?明天还要出团去夏威夷蜜月?不会吧!』,接着倒头呼呼大睡。半夜还被叫起来吵架,因为今天你家的人还做错什么事情,引起枕边人的不悦。专案结案时,感受其实就很像是这样。
当你辛苦了许久,经过了漫长的开发与验收流程,终于让系统上线了。你认为任务已经了结了吗?不,刚上线的系统可能会出的状况最多。所以我们需要绷紧神经,深呼吸,默默地解决所有没有设想到的问题。该解的问题也解完了,不该做的enhancement也做完了,跟客户一直耗下去,终于可以结案了。此时只会觉得如释重负而已。把一个很重的东西从肩上放下来,不会让我们达到高潮,只会让我们喘了一口大气而已。
除了心理上的感觉以外,专案的结案,真的就代表了所有的工作都告一段落了吗?或许我们可以翻开软体工程的书,看看有关于整个软体开发经费的统计图表,如果你真的这样做,你会发现Maintenance通常占了整个软体开发经费最大的部分。
如果我们从经费这个层面看起来,结案之后,应该才是系统开发真正精采的地方,要不怎么我们会花这么多钱呢?可是为什么我们在学校里面,好像很少谈到这一块呢?
我记得小时候上有关软体工程的课程时,在专案结案之后到底还要做什么,好像在书本里面从来都没有提到过。老师通常也是讲完development process,学期也就差不多该结束了。结案之后到底会发生什么事情,god knows。难怪很多初出茅庐,或是没有结过案的人来说,内心总认为只要结案了,我们的任务就已经了结了,感觉起来,好像只要系统上线了,从此大家就可以屁股拍拍走人,每个人都可以就此过着幸福快乐的日子。
这可能也是因为有蛮多专案都来不及活到结案的时候吧。此外,就工程学上的角度来说,这根本就不是讨论的重点。他们重视的,是怎么把东西给做出来,或者说怎么建立一套可大可久的系统,这看起来才有比较大的成就。盖一间雄伟的教堂,画上金碧辉煌的壁画,跟帮伟大的教堂修补柱子,重新替褪色的壁画上色,哪个有成就感,这当然不言可喻啦。
在这一章里,我想讨论一下关于结案之后的一些现象:
 ■很多案子验收时,不管是schedule、scope或quality,都与原先规划的相去甚远
 ■上线之后,才是真正恶梦的开始
 ■scope/resource/schedule的吊诡
 ■文件通常没有随之更新。久而久之,只剩下考古的价值。
 ■系统做的越烂,越有保障。做系统的人吃定客户找不到其他的人可以maintain。
 ■大多数做专案的公司,低估了保固期间的effort。取而代之的,是持续不断的改版。
以下我想针对这几种现象进行讨论。
很多案子验收时,不管是schedule、scope或quality,都与原先规划的相去甚远
***********************************************
如果你找一个正在企业内部管理MIS系统的人,仔细观察他的生活,你会发现他的工作,有绝大多数,就是在于要想办法让现在正在运转的系统可以持续运转,例如每天要帮新来的员工建email帐号,帮他们灌灌作业系统,教教怎么使用公司的系统,装装防毒软体,印印报表,送修机器…除了这种看起来像是打杂的事情以外,所谓的开发工作就是要不停地修修改改,以解决各式各样营运上面的需求。这边多做一张不一样的统计报表,那里改改wording,修修程式的bug所造成的data错乱…。除了要参加无穷无尽的会议,来想办法找厂商把旧系统换掉以外,这几乎是他工作生活的写照。
很多从事MIS工作的热血青年,如果他们在这个工作岗位上待到退休养老的观念还不是太重的话,只要有机会可以把手头上的旧系统换掉,一开始一定手舞足蹈,欢欣雀跃。认为只要辛苦一阵子,就可以万事ok。可是通常等到历尽千辛万苦,系统终于上线之后,常常会发现自己从一个火坑掉入另一个地狱。
我不知道你是否有过使用信用卡的循环信用或者是预借现金的经验。很多人在刷卡时,看到自己心爱的商品,一开始都会看到一个美丽的远景。我只要付出小小的代价,接着就可以解决我现在手头上的燃眉之急。刷下去,就可以开始过我的优质生活了。
通常等到东西买到手了,一直到回家以后才会赫然发现,怎么这个东西也不过如此而已?真是买贵了,还亏我还特地刷卡打算用循环信用哩。没关系,广告跟真实中间总会有一段差距的。反正我急着用,有东西可以用比没有好。接着等到开始要付帐单时,这才发现:『糟糕,这个月又透支了,没办法了,只能动用循环信用。我必须要维持良好的信用,不然就没有信用卡跟循环信用可以用了。那就缴个最低应缴金额吧。』接着内心就会开始暗自垂泪:『我到底还要付多久的钱啊?』『下回再也不要这样刷卡了。』
等到付款的时间过了,内心也忏悔的差不多了,接着就是要奖赏自己的时候到了。嗯,应该可以再次计划一个完美的刷卡购物之旅。
对于很多客户来说,开发一个软体专案,对他们而言,过程中的感受其实跟使用预借现金或是循环信用差不多。一开始时,总会有一个蛮强烈的需求。觉得这个系统一定要解决我们现在手上面临所有的问题。通常要等到上线时,才会发现:『咦,我们花了这么多钱,怎么就只有这些功能?我们还得要付出这么多钱去做enhancement喔?这简直是一堆诈欺的骗子,在光天化日之下,明目张胆的抢劫。』只是这些抢钱的人,不会拿着枪对你喊:『抢劫』。软体公司最常做的是在他们的系统里面埋下一些炸弹,等到你走投无路的时候,再来狮子大开口。所以在开发的过程里面,客户的内心,会不断地在『有了这套系统,我们所有问题都会解决』的天堂与『我简直遇到一堆整天落后进度,追加预算的无赖』的地狱之间摆荡。
或许我们可以从一个假想的专案来体会这个情形。在某次客户内部工作会议中,承办人正在向大头进行报告,讨论这个专案是否要进行:
业务承办人凯莉:要达成总经理揭示今年的目标:『要用创新,效率,以及执行力来重新改造我们的商业流程,让我们可以更具有弹性,可以更迅速地提供给我们的客户成本更低,品质更高的产品。』(大企业教战守则第一条,把大老板的话拱出来是不会有错的。况且大老板的话每次讲的都是差不多的。)我们需要建置一套可以帮助大家更有效率,更迅速做出决策的资讯系统。透过这套系统当作核心,可以帮我们提供更完善的客户服务。也就是说可以透过这套系统,达成高效率,高品质,低成本的需求。当客户满意我们的产品,我们的营收与获利都会获得稳定的成长。公司赚钱股票也就会上扬,才可以创造员工、股东、以及客户的三赢。(反正我就是要做点绩效出来啦,要不年底发股票哪有份?)
大头:你讲的很抽象,依据你的评估,我们有没有什么具体的指标可以衡量这个专案的成效?有没有什么KPI(Key Performance Indicator)可以参考?(官话打完了,讲点人话来听听。)
凯莉:喔,这在我的下一张投影片里面会显示出来,透过这套系统,可以把我们输入订单的时间,从原来的30分钟,大幅缩减到10分钟。跨部门签核的动作,可以透过单一画面迅速完成…(她滔滔不绝的讲了很久,反正会有一堆编出来的好处。重点不在于真正的好处有多少,重点在于你临场时表现的有多镇定,并且曼妙的身材,加上剪裁合宜的衣服,成功吸引了多少色狼的目光。),根据估算,可以节省高阶主管每人每天30分钟的作业时间。预计这种改变每年可以为公司节省七百万以上的费用,还可以让我们回覆订单的时间缩短百分之五十。
大头:嗯,那预计花多少钱?多久开发出来?
凯莉:我们今年编列了五百万的预算,打算在8个月内开发完成。
大头:要这么久?那个黛安,你们那个部门预计今年要做多少营业额?我记得我们上次开会是决定你们要做20亿,不是吗?你对于这个schedule有什么看法?
黛安:老板,你记错了,我们讲好的明明是18亿吧?
大头(装蒜中):哪有这种事?你们这群娘子军这么厉害,多做个两亿不算什么吧。拿出你们的gut来。
黛安(开会都已经讲好了,哪有现在戴顶小高帽就要我多两亿,你当我神啊。每次都这样。):老板,这个问题我们再另外开会讨论吧。我们还是先回到这次开会的主题吧。(不就是要我压缩schedule嘛,这老狐狸。还要拱我出来做坏人。)业务部门的意见是,如果要达成今年的营业目标,我想这套系统会是很重要的关键。业务部门是希望可以在3个月内就可以看到成果。
凯莉(好个黛安,我上次开会前会时,都已经跟你说好,最少要半年了,现在来个3个月):报告副总,我们已经评估过了,八个月已经没有什么buffer了。
大头:我们也还是要照顾到业务部门的需求啦。这样吧,我们先以半年内上线为目标,剩下两个月就当做是buffer。既然时间从八个月改成半年,我想应该花个三百多万就够了吧?不要超过四百啰。黛安,那个营收的目标,我再找你讨论。
凯莉想,Yeah!Bingo!Just make:报告副总,没有问题。这个案子如果您核准了,我们就可以开始进行后续的发包作业。
接着这个案子以385万包出去。厂商一开始报了一年的工期,接着被凯莉要求要在3个月内做完。最后成交的deal是在4个月内要可以上线。
布鲁斯刚看到合约,吃了一惊:四个月?哪有可能?不是估了最少要一年的吗?
吉娜:没办法,业务部门已经尽了最大的努力了。现在景气不好,也没办法挑案子了。
四个月后,project当然delay了。凯莉好好地把布鲁斯跟吉娜修理了一顿。不过内心暗自庆幸:『还好我还有两个月的buffer。』于是在status report中,还是报告:『目前进度虽然落后,不过vendor已经努力赶工了,所以应该有机会在期限内完工。』
又过了两个月。系统依然还没有开发完成。才刚开始进入单元测试。
吉娜:布鲁斯,penalty已经要开始了,你们现在到底状况怎么样啊?
布鲁斯:我们刚把程式写完,要开始做unit testing啦。我每个礼拜status report都跟你提过,这你也知道的啦。
吉娜想,真是个不懂得人话的呆瓜:我是说,因为我们有签处罚条款,如果延迟验收测试的时间,是每天要罚钱的。你们现在到底有没有东西可以给人家测啊?我们早一点进入UAT嘛。反正只要开始进入UAT,就不会被罚钱了。反正只要系统会动,就可以给user测了嘛。不管找出什么bug,都是正常的嘛。反正UAT哪有可能没bug。当初在签合约的时候,我早就预料到这一点了。
布鲁斯想,又要这样玩喔?这样玩不是已经玩死很多次了吗?怎么永远都学不会啊?:好啦,如果你真的坚持,我安排下个礼拜开始就进入UAT。
吉娜:Good. Go ahead.
如果你没有在出厂前,把unit test/ integration test…内部的alpha test做完,为了赶进度就开始到客户端进行验收测试,提早进入UAT,对大多数人来说都是痛苦的折磨。进来测试的user个个都发现这是个可怕的地狱。
咦,怎么这个键不会动?咦,怎么流程跑一半就走不下去了?咦,资料怎么都是错的?啊,我这个月的事情做不完了,还要来测这个烂系统喔!
接着当然就会去review bug,依照大公司的习惯,咱们就来个defect review meeting吧。
在这次的defect review meeting上,布鲁斯报告了defect的种类与数量,以及一些关于defect的摘要资讯。
使用者代表辛西亚首先发难:我必须这样子说,这根本就是在浪费我们的时间,我们要测一个完整的scenario,根本就测不下去。我连最基本的输入作业都走不完,这样也要叫我们测?
使用者代表苏珊:对啊,这样的品质怎么有可能继续测下去?凯莉,你说呢?我们浪费了三天的时间也。三天也,没有一个flow是可以走到完的。
凯莉:布鲁斯,你们到底有没有测过啊?你们这样不行啦。自己都没有测好,怎么可以交给我们测?
布鲁斯想,我也知道会有这个问题啊,不过吉娜就要我进入UAT,我又不敢说不要:有啊,我们内部的测试人员有进行测试啊。(是啦,有测啦,只是连单元测试都没跑完。昧着良心讲话,这又不是第一次。)
凯莉:这差太多了吧。你摸着你的良心讲,我们打开天窗说亮话,这样真的有测过吗?你们还没有ready,就不应该浪费我们的时间。
布鲁斯做了不少辩解,不过后来的情况就是再多安排了几次新的schedule,以及新的UAT。与此同时,使用者越来越了解系统,对于系统也越来越没有信心。接着就越来越觉得这个系统跟他们要的不一样。
很多change request,就这样偷偷地溜了进来。因为schedule delay了,布鲁斯对于change request的抵挡能力也被剥夺了。吉娜答应了几乎全部的enhancement。UAT从原来的一个月,延长到超过了8个月,转眼之间系统已经开发了一年四个月。凯莉也因此被大头们盯的满头包。
或许我们该看看凯莉这边的故事。刚开始时。
大头:凯莉啊,到底你们这个系统什么时候会好啊?
凯莉:报告副总,我们现在遇到了一些问题,我想您也知道,系统开发总会有一些不可预见的问题。现在厂商已经积极在处理了,我想这个月应该就可以有初步的成果。
大头:要好好加油喔。这个系统很重要,我们业务部门已经跟我讲过很多次了。希望它可以早点上线。有什么题需要帮忙的,尽量让我知道。
就一开始来说,大家都没有什么deadline的压力,所以气氛一片祥和。接着是project Delay了一个月。
大头:凯莉啊,怎么这个系统还有这么多问题啊?现在状况到底怎么样?
凯莉:报告副总,我们现在已经进行了一次UAT,user已经找到不少bug,厂商正在修,我想我们修好了以后,可以在这个月再进行一次UAT,这次找到的问题应该就可以解决了。
大头:push他们一下嘛。有必要找采购去跟他们sales谈谈。他们的主管是吉娜吗?我来给她拨个电话。
一开始delay的不算严重时,大多数的人都可以接受这种事实。因为每个人多少都为这件事情抓了一个buffer。不过一旦超过了这个buffer,就会开始不悦了。我们可以看看Delay了两个月时的情形。
大头:凯莉啊,你不是说这个月会再完成一次UAT吗?现在状况到底怎么样了?
凯莉:报告副总,我们现在已经再进行了一次UAT,不过这次还是没有达到我们的标准。
大头打断:那你有什么action plan?我们除了被动等他们修以外,还有什么可以做的?黛安已经跟我complain了很多次了。做事要有方法啊,我们怎么可以被动的一直等他们修?你要主动掌握他们的进度啊。好,从现在开始,你每个礼拜跟我报告进度。还有,黛安跟我说,你们做的根本不是他们要的。
凯莉:报告副总,他们现在提出来很多东西,都是先前我们在规划这个系统时,根本没有提到的。在需求访谈时也没讲啊。
大头发脾气了:真的吗?不过因为这个案子的delay,已经造成他们作业上的困难,你还是要请厂商解决啊。不然因为project delay造成的问题是要谁来解决?是你来解决吗?难不成是我?搞什么飞机啊?公司找你们来,是要你做什么的?
凯莉被凶了,不敢再多说什么:我会尽力配合业务部门的需求。
通常,大头们都会相信只要挤的够大力,公牛都会被挤出牛奶来,如果这时你身陷在这个专案中,最大的差别,就看你是那匹倒楣的公牛,还是无可奈何的挤奶工人。相信我,两个角色都不怎么营养。
于是凯莉开始要求布鲁斯每个礼拜交一份status report,以便让她可以跟大头报告进度。同时她开始软硬兼施地要求布鲁斯要吸收change request。
又过了一个月。
大头:再来看我们手上这个麻烦,凯莉,你说说看现在的进度是怎么样。
凯莉:业务部门提了总共有72个enhancement,厂商评估过了,这还要再三个月才做的完。
大头发飙:三个月?那我今年的quota怎么办?你来背?
凯莉默默无语。
大头还在不爽:你们这些人做事就是不用脑袋,一点都不知道变通。你叫厂商想办法先上一个版本。你跟业务部门讨论一下,先把主要的功能先上。剩下的我们慢慢再说。
凯莉:是。
一个phase的系统,变成了两个phase,上第二个phase的时候,你就得要把现在的production data,migrate到新的系统上。而两套系统都需要测试,就会吃掉那已经很忙碌的end user的时间。所以UAT的进度会落后的更多。很多出发点是想要让系统变好的决定,即使立意良善,也会因为没有考虑到的边际效应,造成了完全相反的结果。这种事情在开发软体系统时,屡见不鲜。
好吧,又过了很久很久,终于可以上线了。
大头:凯莉,现在怎么样了?这个案子到底还要拖多久?
凯莉:业务部门的enhancement终于都做完了。而且我们UAT也过了,这一整个round都没有severity 3以上的defect了。只有10个severity 4的minor defect。
大头:喔?这倒是一个好消息。这段时间你辛苦啦。等到上线以后可以规划一下嘛,出国去散散心啊。
凯莉:谢谢副总。我这几天已经跟各部门讨论上线的时间,包含人员训练的时间,机器什么时候要shut down,migrate data…都已经在跟各个部门讨论,等到有了共识以后,我会把整个系统的roll out plan寄出来。
大头:不要忘了还要写一份contingency plan。比较major的task要做一份check list。等到这个系统launch,你就可以好好休息一下。
系统终于上线了。刚上线的一个月,布鲁斯的整个team,几乎是日以继夜地待在凯莉的公司里。终于,经过了两个月,系统已经稳定下来了,这时候也开发了一年半了。
布鲁斯:凯莉啊,我们配合了这么久,你就不要再卡我的尾款了。
凯莉:可是我们还有25个minor bug还没修也。况且上次你们程式错乱,造成我们资料不正确的资料你也还没帮我们修啊。
布鲁斯:你又不是不知道这里有多少其实是enhancement,唉,反正我们都吸收那么多了,不在乎多个一两个啦。你先把这个案子验掉,让我也做点业绩。况且我们还有一年的保固期间。算帮我一个忙。我们都已经一起co-work这么久了,你就帮帮忙吧。
凯莉:好啦好啦,可是你不可以黄牛喔。那些乱掉的资料一定要帮我们改喔。还有这些bug,要帮我们修掉。
布鲁斯:没问题。
经过布鲁斯求爷爷告奶奶一个关节一个关节的打通,这个案子终于验收了。这还算是有善终的。我不只一次看到开发专案的客户与专案团队,在投入了大把的时间与精力以后,草草结束。除了结果不如预期以外,所有投入的心血跟精力,几乎也等于白费了。留下来的当然也是一堆难解的烂摊子。如果想尽办法让它硬上了,也结案了,接着要面对的就是无穷无尽的bug以及enhancement。
当你的scope,schedule,跟quality都与你原先规划的差很多的时候,就等着再另外起一个enhancement的案子吧。就是因为有很多案子都是丑丑的上线与验收,这才让maintenance会变成一个无止尽的大黑洞。
上线之后,才是真正恶梦的开始
***********************************************
不管你做了多少测试,新系统上线,总会有你意料不到的惊喜。
有些时候,是因为你在测试时,并没有办法真实地模拟实际的状况,例如一个网站,尖峰时刻同时上线的人可能有一千人,可是你没有足够的资源,可以让你模拟同时间有一千人连上线的状况。因此当实际上线之后,这一千个人加起来,可能就把你的系统给弄死了。
这让我想到曾经在报纸上看到的新闻。大多数的男生都还蛮喜欢看A片,即使称不上喜欢,最少也不排斥。不过前一阵子新闻报导指出,警察查获大规模的A片工厂,起出了数以万计的A片。因为办案的需要,警方得要进行蒐证,也就是说每个人要日以继夜地看完数以千计的A片。他们得要整天盯着A片快转,然后把会引起生理反应的精采片段,擷取下来,做为呈堂证据。几天下来,每个负责做这种事的警察,每个都性趣缺缺,人人都觉得是一种精神上的折磨。
软体系统也是一样。有些人写的系统,在使用人数不多时,看来一切正常,就跟正常男人悠闲地在家里面看A片一样地轻而易举,是一种令人愉悦的事情。可是当使用人数多到一种数量时,这时就跟我们短时间内看了太多A片一样,系统就会受不了,做出不太对劲的反应。
熟悉测试理论的人都知道,这就是为什么我们要做压力测试的原因。对于压力测试在做什么不太了解的读者,我可以用这个例子来解释,就是把一个男生的精子先榨干,接着开始放映一百部A片,要他盯着萤幕去看,而不会睡着。
在台湾,通常我们不会做压力测试。即使做了,也大多都是做做样子。因为我们大多认为,我们的运气不会那么背。况且,对一个系统这么残忍,这是一件多么不人道的事情啊。不过实际上,你对系统仁慈,就是对自己残忍。没有好好测试系统的极限,等到上线后再来收拾残局,通常都是一件很辛苦的事情。
一般而言,即使我们要做压力测试,我们也找不到那么多的人来做真实的测试,所以大多是靠软体来模拟。然而透过软体来模拟,毕竟还是跟真实的状况有差距。再加上大多数的系统都会有上线的压力,所以稍微变通一下,总是可以想出一些办法,做出可以接受的数据,这种技术上的犯规,对大多数人来说,都是可以接受的结果。反正在大公司里面进行报告时,忙碌的大头们多半就在看你最后的数据,谁会看你的假设?这就跟很多软体在比较效能时,每家都可以宣称它在某方面赢过人家是相同的道理。基本上,没有人说谎,只是我们为了达成数据背后所做的努力,是你们没有办法想像的。
另一个问题也很类似,是资料的量的不同。我们通常为了测试时验证资料方便,资料的笔数会依照测试个案的数目来决定。真实的系统可能有上百万笔资料,可是我们在测试时,可能限于人力与时间,了不起就是有个近千笔资料。百万笔资料跟数千笔资料有着非常大的差距。因此,上线时遇到一些surprise,这也不是什么了不得的事。可是真实上线时,任何这种surprise,都会影响使用者的信心。
笔数太多所造成的最常见的问题,就是系统的performance不好,整个系统跟死掉差不多。User没有耐心以后,就想办法把跑到一半的东西中断掉,重新再来一次。跑到一半的东西,再重来一次,这可能已经远超过开发人员的想像了。会有什么结果,通常谁也不知道。
例如有些人在资料库的操作上下了max这类型的查询,想找资料库里面的最大值。在笔数少时,当然运作的轻松愉快啦,真的上线时,一旦遇上了,你就会听到硬碟呻吟惨叫的声音。系统会赏你一个漏斗,搞不好数十分钟过去了,还是一个漏斗。很多人一看到这种状况,直觉就觉得,这一定是系统有问题,系统当掉了。
根据我们从小使用电脑所累积的经验,很多人还会伸出三根手指头,卯起来把机器重开,再试一次。弄个几次以后,接着通常就会从三根手指头,转成开始骂三字经了,外国人可能会把三根手指头改成用一根中指来替代。有些时候,系统还真的有error message,例如transaction time out那一类的。这时候当然就会上演三字经加长版。举凡这种鸟事,通常都在真的上线时才会遇到。
除此之外,使用者的操作方式,或是使用者所使用的电脑超出预期,也是上线时常见的事情。
很多程式设计师的电脑,配备可能都不错,特别是这年头硬体越来越便宜,所以大家用的电脑越来越好,帮自己的电脑升级,这是所有软体开发人员最大的嗜好。如果不多插一点RAM,实在是对不起这么便宜的硬体。
可是在很多大公司里,很多end user使用的,可能会是你无法想像的老旧配备。上面可能是灌着一些几乎已经快要绝迹的作业系统。遇到这种各式各样的状况,常常会有你无法解释的灵异现象会发生。例如可能这套系统有几百个人在用,没有其他人有问题,就只有一个人的电脑,每次跑你所写的程式就会当机。我们为了这种懒得解决的东西呢,发明了一些术语,看起来比较有文化气息一点,就会说这两者『incompatible,就是不相容啦』,没那么正式的说法是,这两个东西『冲到』了。(作者注:有人说是叫相冲啦,不知道是不是取互相冲突的含意。不过不管你怎么写,应该都解释的通。)
我常常想『冲到』,这个名词到底是从那儿来的。当我看到最近越来越多的命理节目之后,才恍然大悟。所谓这套系统跟你的电脑上面所跑的应用程式冲到,指的就是你的八字跟这套系统不合,就跟小孩子跟父母相冲,就会克死家人一样,是遵循着相同的理论。所以如果你跟一套系统之间犯冲,每次只要是你要用,系统就会出问题;而别人要用,就不会有问题。这表示你会遇到这个bug,最大的原因就是你的命不好,前世又没有好好修行,这辈子才会遭此劫难。准备好三牲酒品,应时水果,加上金银财宝,再把电脑扛去庙里,请神明帮忙收收惊,再捐个一千块点光明灯,应该就可以改善这个状况了。(注:我去翻了一下java周报,,才看到洪志鹏先生也有类似的意见。果然是英雄所见略同。嗯,没想到这么容易就可以自吹自擂地跻身英雄的行列。)
我遇过不少programmer,每次跟他们提到:『嘿,我在使用系统时,遇到一个问题。』他们典型的反应通常是:『我的环境不会啊。』『在我的环境下一切正常啊。』『一定是你操作有问题。』等到确定是因为环境造成的,他们通常的反应就是耸耸肩:『早就跟你说,这又不是我的错。这是因为环境不同造成的。』
大多数的人会视这种问题的存在为理所当然,所以也就没有什么预防跟改善之道,通常就是遇到一个解一个。遇到不会解的,就可以宣称,这是微软的问题,你的windows需要重灌…。我是不知道微软背了多少恶名,不过既然每个人的电脑里头,大多都用他家的作业系统,暗示问题出在他家身上,应该是很管用的招数。
这年头,每个人的电脑里,到底存在多少奇怪的东西,实在是一个很难管理的难题。这当然会使整个专案的不可预测性增加,不过话说回来,专案管理一直都还不是一门精确的科学。『行到水穷处,坐看云起时』一直是很多人的最高指导原则。反正就是且战且走嘛。
通常这种因为环境、操作顺序的问题呢,它的严重程度(severity)呢,是依据发现者的官位有多大来决定的。大头们发现的,即使内容再无聊,都会被列为severity 1。反过来说,如果你人微言轻,就会被放在defect queue的最后头。
就是因为正式上线之后,所有意料之外的东西才会浮现,所以一个系统刚开始上线时,通常就是恶梦的开始。当然,有很多方法可以降低正式上线时的风险。例如跟银行有关的系统,通常在测试以及系统上线的部分,会采取比较高的品质标准。不过对大多数其他种类的商用系统来说,因为赶schedule已经成了家常便饭,所以通常不管你做了再多的准备,多多少少总会有一些遗漏掉的部分,会在上线之后才跑出来。
这就跟新婚的夫妇,通常要到结婚后,才会发现枕边人一些不为人知的小秘密一样。有些人为了避免这种婚后才开奖的surprise太多,会想要先来个同居。殊不知同居可以降低surprise发生的机率,可是很难全然避免。同样的道理,你可以进行很多次的pilotrun,可是正式上线时,还是多多少少会蹦出一些surprise。
&选美小姐机智问答&你跟同居很久的男人终于结婚了,等到要办理婚姻登记时,才发现一个天大的祕密,请问下列哪一种状况最让你吃惊?
 A.他还没离婚
 B.他是个女的
 C.他不但是个女的,你一直以为他的肚子大不过就是个啤酒肚,其实肚子里,还有个快要出世的小婴儿
A这种答案,看起来就知道是来垫档的。同居的人另外还有家庭,这根本太常见了。不足为奇。B果然已经出乎意料之外了,不过C…这哪有可能?好吧,我选C。
根据宇宙小姐选美协会提供的参考答案是:『主持人,你一定在说笑吧,出来选美的人怎么可以跟人同居呢?这样会被撤销资格喔。像我这么品行优良的人,顶多就是因为自幼家贫,三餐不继,只好在酒店里面上班当会计而已。我要特别声明,我可没有坐台喔。你看到周刊记者所拍到的那一个削凯子的人啊,那是跟我长的很像的孪生妹妹啦。』
虽然花木兰一直到退伍时,才被人发现她是个女的,这种事情简直令人难以置信。不过现实生活中,最大的surprise都是在开奖后才会涌现。很多人因为有了丰富的买彩券经验,也就都可以体会上线之后,才是恶梦真正的开始。
不过上线一阵子之后,恶梦应该也跟着结束了吧。很多人都是这样想的。不过事情通常都不是如此单纯。这跟买房子付贷款差不多,房子不是付完自备款就了结了,真正的考验,是后面每期每期要缴的贷款。这就牵涉到下一个主题:『Scope/schedule/resource的吊诡』。
Scope/resource/schedule的吊诡
***********************************************
我们在开发一套系统时,会进行各式各样的规划。你会定义它的scope,决定要你会想想要花多少resource,有什么milestone要meet,每件事情,都会编造一个明确的schedule。可是对于一个结案之后的系统,你会做些什么样的规划呢?
通常,对于一套production system,大家会特别注意的plan,都是关于怎么样才能维持这套系统的运转。比较有规模的公司,会去规划要怎么做资料的备份,还原啦,system administration时,应该遵询什么规则,遇到问题时,应该要怎么反应,遇到紧急事件时,要有紧急应变计划(contingency plan)…
可是对于跟整个系统功能面有关的问题,除非有改版的计划,否则我看到的通常就是:『这套系统现在上线了,我们就找个专门的人来负责维护好了。』或是:『你就把这个问题当做是一个bug,发到问题追踪系统(bug tracking system)里面,我们再来follow好了。』
因为我们通常都不会对一套已经结案,没有任何明确改版计划的系统进行什么规划,所以大家对这方面通常都以等闲识之。『反正你就找个专人去负责。从今以后,他就负责这套系统,任何时候,这套系统出了问题,你们找他就对了。』嗯,对主管来说,这真是piece of cake。比较起来,怎么开发一套系统,感觉起来才是一门学问,维护一套系统,感觉起来就比开发系统容易多了,所以很多吊诡的现象,就会在这段期间不断地涌现。
第一个问题也是最根本的问题:『scope的不明确。』
Scope的不明确
***********************************************
如果我们先把enhancement摆在一边,那么维护期间要处理的,就是把系统的bug给解掉。对于一套已经在run的系统来说,到底会出什么bug?这其实很难做估计。通常我们能做的,顶多就是把bug按照严重程度(severity)来分级,再依据对于作业面的影响,排定优先次序(priority)。
问题是,什么时候会遇到severity 1的bug?除了老天爷以外,谁知道?
正因为这个问题的不可预测,所以我们都是遇到了问题解问题。
可是『遇到再说』…这算什么planning?
有些人会期望可以从统计学上来看这个问题,不过一般而言,大多数人会把注意力就直接放在改版上。既然不可预测,我们就发挥聪明才智,让这套系统可以撑到改版就好了。改版的scope,就把这段期间发现的bug一起包进来,再加上一些enhancement,这样就ok了。既然是改版,就会有一张新的合约,新的预算,也会有新的project team,会为这个新的任务来进行规划。既然有了新的project team,就会重新就各个层面来思考这个系统现在跟未来会遇到的问题,大家也就不用伤这个脑筋了。
对于还有足够的resource来进行改版的系统来说,这未尝也不是一个solution。不过对于一套没有改版计划的系统来说,就只能赌运气了。更何况,很多系统,都是在有了enhancement的规划之后,又浮现新的bug或是新的enhancement request。
凯莉:布鲁斯,你们的这个系统怎么这么不稳啊?user又跟我report又有一个bug。
布鲁斯看了一下,理直气壮地说:等一下,这个明明就是个enhancement。
凯莉:这那是enhancement?
布鲁斯:要不你拿出证据来,根据我们的分析文件,系统现在运作的很正常。完全符合我们分析文件上的规划。你们现在讲的,根本就完全不一样了。
凯莉:user跟我说那是你们的analysis document根本就不符合他们的requirement?好啦好啦,这种东西我们已经吵了很久。反正我们现在已经针对这套系统起了一个新的enhancement project,你就把它顺便包进去吧。
布鲁斯:这样我要修改我们的报价。这个enhancement最少需要三个人月才有办法做完。
凯莉:啊你嘛帮帮忙,你们这么强,这哪会需要三个人月?你不要忘了,你们的enhancement才刚开始报价。我们可是还没签约啊。
布鲁斯:要不你们enhancement可以找其他厂商来报价啊?(我就看你可以去找谁。系统做的不好就是有这个好处啦。)
凯莉:哎呀,我们合作了这么久,你就不要这样子嘛。顺便在enhancement project里面帮我们做掉嘛。我们今年真的是已经没有足够的预算来做这个案子了,连enhancement都是我们处长到处张罗来的。况且,如果你要再改报价,那合约大概就要再重跑一次流程,你们最快也要再一个多月才会拿到头款。帮个忙啦,现在有人手就先做掉啦。
布鲁斯被打到罩门,吉娜才刚刚跟他说,公司最近现金有点紧,希望可以跟客户拿一点现金来帮助资金调度:我跟development team研究看看。
对于一套已经在running的系统来说,维护的人到底要做些什么其实很不明确。而最讨厌的,是政治力的干扰会特别严重。一个做好的系统,就像是个练功的靶子一样,任何一个高阶主管,只要想证明他的政治影响力,或者只是单纯的无聊,都可以发表他的高瞻远嘱一下,而这些东西,通常会被当做priority 1的issue来处理。
处长请他的秘书写了一封email过来给凯莉的老板:『你们的这套系统是比旧系统好用啦,不过在主管签核后,不要每张单据都发一张notice出来嘛。这样我的email都快被你们这些垃圾信挤爆了。』
凯莉的老板收到信后,马上召开了一个紧急会议。会中决定,这是top priority issue,因为『处长讲话了。而他唯一的comment,是这个关于email的小问题。我们当然要尽力而且尽最快的速度,达成user的需求。』于是布鲁斯,用最快的速度,把人抽出来解决这个小问题。这个简单,反正就是把信都档掉就好了。
新功能上线没多久,稽核室写了封email来:『这样不符合我们内部控制的原则喔。』请示过处长后,处长说:『你们这些笨蛋,我只是叫你们把多封email合併成一封,谁叫你们把email notice停掉?』
这件事当然被当作top priority处理。新功能上线头一天,总经理的秘书转寄了一封信给处长,处长打开一看,里面只有一个问号。下面则是几位副总经理的假单。当下立刻大吃一惊。原来布鲁斯他们在赶工时,没有把总经理的email排除在外,想想看,总经理是日理万机的人,哪会需要看这种东西?好死不死,今天他心情好,除了秘书以外,他自己还在看看他的email信箱有些什么信。就被他发现这个bug啦。整个project team因此被骂到臭头,这也不是什么意外的事了。
就是因为你要做的东西大多是一个一个很琐碎的需求,所以杂事特别多。做这种杂事,是非常没有成就感的。这也就引出下一个问题:『大多数开发系统的人,都不想maintain别人的系统。所以我们通常都派最资浅的员工去maintain一套系统。』
大多数开发系统的人,都不想maintain别人的系统。所以我们通常都派最资浅的员工去maintain一套系统。
***********************************************
古人练剑时,练到极致时,常有所谓剑在人在,剑亡人亡。不过根据我的观察,这种东西在资讯业界并不存在。取而代之的是:『系统在,人绝对不在。任何一个了解一套系统的人,都会因为某种原因离开这套系统。接着,系统就会随着时间的推移,移交到一个超级不适任的人手上。』(作者注:在物理学上,这好像叫做热力学第二定律。)
一个人可能会因为一套系统开发的很好,所以高升去做小主管了;也有可能是因为他的薪水,随着年资的增长而成长,所以他被调整到需要背负更重大的责任;也有可能是因为他自己想要多摸一点其他的东西;或者是他受不了这么无聊的工作,就屁股拍拍走人了。可能的原因很多,不过每个人都会因为各式各样的原因而离开他所熟识的系统,换个菜鸟进来。
如果菜鸟接的不错,没多久,就会因为类似的原因被调走,再换一个菜鸟进来。反正每个系统早晚都会遇到一个终结者,会让这套系统在他手下开始沉沦,接下来的连锁反应,则是会让这套系统走向万劫不复的境界。
通常的模式是这样子的状况:
1.头痛医头,脚痛医脚。因为不熟,所以改出一堆问题。
2.改出的问题,越来越多,菜鸟没有能力解决。
3.开始没有力气去更新文件
4.开始严重落后进度。主管被骂了,所以会找个熟手来帮忙。
5.熟手解决了一部份的问题,可是因为他不了解最新的状况,所以埋下一些他自己也没意识到的炸弹。接着系统看起来回光反照了一下,重新展现出生命的活力。接着熟手就因为阶段性任务结束,就回到原来的工作岗位。
6.炸弹爆了,菜鸟继续去解。
7.菜鸟因为一直解不掉,又被人K到受不了,递辞呈了。
8.没有人知道菜鸟到底对系统做了些什么。
9.熟手硬着头皮去乱改。拖过一阵子后,赶快找机会开溜。主管找不到愿意接手的人,找个更菜的菜鸟来接手。
10.回到2。一直到这个系统被时间淘汰。重新做的人,通常跟原始的team已经没有渊源了,所以犯过的错误,吃过的苦头,会不断地重复出现。
所以就resource来讲,最适合的人通常都待不久,不适任的人,则是会一直滥竽充数下去。这也让maintenance变成一个越来越难解的难题。基本上这会是个恶性循环,不过要打破这种恶性循环,要改变的观念实在是蛮多的。组织要透过学习来导正这种观念,其实是一件很困难的事情。更何况很多人根本没有意识到这件事情的重要性。
除了人的素质以外,另外一个很有趣的问题在于人的数量。一套系统,可能开发时总共有十个人在开发,每个人写了个三万行程式,所以总共有三十万行程式要维护,可是到了维护阶段,我们常常是只剩下小猫两三只在接手。一套系统只有一个人维护的状况屡见不鲜。
在开发阶段,每个人只要负责三万行,可是到了维护阶段,一个人要接手三十万行的东西。就比例上来说,实在是很不相配。
你可能会说,接手的人,并不是在创造新的东西,只是需要拾人牙慧就可以了。这相对来说比较简单。问题是,当系统出问题时,你搞不好就是要把别人花了许久的心血给钻研透彻了,才有办法找出问题,并且解决问题。三万行跟三十万行,这中间的难度差异,相去不可以道里计。
你要改别人的系统,照理说你得把别人的东西都读通了,才有办法开始修改。不过系统出毛病时,通常你的各级主管们,根本就等不及你对系统有任何概念,就会要求你开始解决问题。然后站在你的后头,盯着你的萤幕发呆。很多人在赶鸭子上架,不知道从何下手时,就会选择比较安全的做法,也就是站在古人的基础上继续下去。所谓的叠床架屋,就是这么回事。
或许你不清楚什么是叠床架屋,我在此做个小小的说明。
系统的程式不晓得哪里出错了,原先不应该寄给总经理任何信件,现在却会把副总经理的假单寄给总经理,要求他来核准。这时候最简单又最快的做法是什么?找遍所有的source code去看bug在哪里吗?Oh, my god.当然不是。答案是在要寄信之前,凡是遇到要寄给总经理的状况,就把这段程式给跳掉。先看是不是总经理,如果是的话,套一句韦小宝的名言:『老子不干了』。不寄给你总成了吧?我们不过多设一个参数,叫做『总经理的email』,接着再多一个if then else的判断而已。Piece of cake,no big deal。
//start for defect 198
if document.email =总经理的email then
system.out.println(‘这是大头目啦,老子不寄了’) //不要寄信给大头目啦
document.sendmail()
//end for defect 198
很多人就这样卯起来改bug,把原有的结果丢到资料库,还是丢到档案里面,接着写另外一支程式,去把原有的结果读出来,加上我们要的新逻辑,再丢到一个新的结果档里面。或者是直接拿新的结果把旧的结果给覆盖掉。问题的根源呢?可能谁也没力气去改了,那就这样吧。反正问题已经获得了解决。
多几个人这样搞下去,整个程式的架构就变成一团混乱,谁也搞不清楚,原来写的人,到底想干什么,后来改的人,又是为了什么狗皮倒灶的原因,又写了一堆有的没的周边程式。今天把总经理的email跳掉;明天又因为某个部门在抱怨,所以遇到这个部门的主管时,要cc给这个部门其他的主管;后天又要把mail密件附送给部门的秘书…这样一路下去,系统就这样越长越胖,越来越慢,变成一支可怕的巨兽。
所以对于维护一套系统来说,Scope难以界定,resource良莠不齐而且人力不足,接下来受伤的当然就是schedule。偏偏schedule又都很短。这就带出另一个吊诡:『问题越来越困难,可以用来解问题的时间,却越来越短。』
问题越来越困难,可以用来解问题的时间,却越来越短。
使用系统跟使用循环信用差不多,你用的越多,你对它的依赖程度就越大。可是你的胃口,却会被循环信用慢慢撑大。直到你无法解决。
在某家不大的公司里,现在是某个月的1号,使用者玛莉使用她的系统来产生月报表。就在这时,她发现系统有个bug,该显示的资料没有全部抓出来。她马上打电话给公司里面负责维护系统的MIS工程师艾力克。并且解释了她所看到的状况。
玛莉:你们这个bug要修多久?
艾力克:关于这个问题,如果真的要我讲一个期限的话,我想……会是一万年。
玛莉:该死,不要开玩笑啦。我这个月5号要交报表。而现在笔数根本就不对。
艾力克:我看了一下资料,这些资料都还在,应该问题不大。我明天赶给你看看。
玛莉想,嗯,明天就有了,这样很好啊:ok。
过了两天,玛莉拿到她要的报表。虽然晚了一天,不过这个月顺利过去了。又过了几个月。玛莉发现这张报表的数字不合。简单的讲就是明细加起来的数字,跟总计不对。而她因为报表已经顺利印出来了,就没去管它,先去忙别的事情。一直到三号才有空去对报表的数字。这才发现事情不好了。连忙打电话给艾力克。
玛莉:要死了,要死了,这张报表的数字不合啦。
艾力克:莫惊莫慌莫害怕,施主切勿心急,待贫僧仔细观来。
玛莉:哎呀,我后天就要交报表了啦。完了完了,赶快赶快啦。
艾力克:关于这个问题嘛,让我仔细思考看看…
玛莉:啊你嘛帮帮忙,不管啦,你今天一定要修好啦。我陪你加班。
艾力克:好啦好啦。我今天赶给你。
艾力克连续抓了两天的bug,每天只睡两三个小时,终于让他抓到问题的所在。这次运气不错,让玛莉顺利在五号交出报表。到了年底,系统出了其他的问题。而这一次,很大条。
玛莉:艾兄,大艾先生,帮忙喔,救人喔。我的系统当掉了啦。我执行年度作业以后,整个系统当掉了啦。
艾力克:你跟我讲一下操作顺序。
玛莉:我就年度作业给它run下去,系统就跳出蓝色萤幕,要我通知系统管理员,接着就自己重新开机啊。我现在开机以后,连动都不敢动它。就赶快打电话给你啊。
艾力克:这么严重啊,你不要吵我,我过去妳那里。
艾力克到了玛莉的电脑旁,开始认真的研究,接着在旁边把他自己的notebook连上网路,看系统的log。他看了将近一个小时,脸色凝重,平时的嘻皮笑脸已然不见。玛莉在一旁,不知说什么才好。终于…她忍不住了。
玛莉:这个问题很严重吗?
艾力克:嗯。
玛莉:什么时候才会好?年底到了,我要报资料给财务部说。
艾力克:不要吵啦。你刚刚run的东西,刚好是在执行server上一堆stored procedure。这支跑到一半当掉,我还在看log,看看到底跑到那里,要怎么recover。
玛莉听不懂:喔。那你要帮帮忙,快一点喔。加油!加油!加油!我去买杯饮料给你喝。
这次的问题比以前都严重多了。系统执行的程式,把玛莉电脑里的记忆体吃光了,接着开始使用硬碟来存取资料。而玛莉的硬碟在惨叫很久之后,产生了坏轨。所以系统送她一个蓝色萤幕当纪念品。很自然地,这次的问题,花了快两个礼拜才恢復正常。
一套系统run久了以后,每个人都会把它会正常运作视为理所当然。即使是bug很多的系统,run久了,每个人对于会遇到什么样的bug,大概心里多少都会有个底。所以系统执行到半天,它就会当。没关系,重新开机后再重来一次就会好了。这个功能系统会做出一些错的资料,没关系,反正一个月就是个几十笔,我们人工改改就好了。Life will find a way.So is the system.我们总会找到一些方法让系统可以跑下去。
不过用久了,系统常常就会在你意想不到的时候,跳出几个罕见的大问题。因为我们已经习惯了有个可以预测的系统了,这时如果出乎你意料之外,通常可以反应的时间就会很短,可是要解决的问题,因为前所未见,所以可能不是那么简单。时间通常只够找出最快的turn around solution。让系统可以活下去,继续用。
做出turn around solution后,我们通常不会有时间做很正式而完整的测试。然后就把这个patch上到系统上去。很有可能就在这个时候,会引进了新的问题。所以我们就会急急忙忙地花大多数的时间,尝试着去修正错误,与此同时,制造出更多的错误。
所以对于一个正在运转的系统来说,stability才是最重要的。要是它没坏,干嘛修呢?多一事不如少一事,少一事不如根本就没事做,没事做的话,还可以利用上班时间,上网看没穿衣服的美少女。这是MIS人员特有的福利。于是乎,你根本就只是应景的提出一些改版计划,或是一些无关痛痒的小幅更动,最好不要替自己找麻烦。养老的心态越强,就越不会对于维护系统做什么长远的规划。
Scope/resource/schedule之间的互动,就是专案管理最重要的几个关键。可是对于一套进入Maintenance mode的系统来说,我们其实在这个地方思考的并不多。那也就是为什么,一套系统的maintenance cost会是整个life cycle里面最高的。不过要克服我们这一节里面所遇到的吊诡,并不是那么容易的事情。因为它牵涉到的,主要是管理者的既有观念。而观念的改变其实是相当困难的一件事。
有很多外行的主管,压根儿就不知道民间疾苦,跟贫户之子一朝飞上枝头做凤凰之后,就忘了有个公平的联考以及人人有能力上大学,是多么好的一件事一样。这样说起来,从基层做起的主管,应该就会表现的比较好。
这也不然。从基层做起的主管,在思考维护系统的问题时,常常会拿他自己对于系统的认知,来进行估计与指引。这时候错误的认知,其实会影响他做出正确的判断。
布鲁斯:这个问题,只要把这两个table的东西join起来,另外create一个table,再把结果insert进去,接着,再写程式去把这个table的东西读出来,再做点加工就好了…嗯,我看应该一天就可以写完了。这样吧,我想我们就订两天写完好了。
丹尼尔心想,那有可能?:……
布鲁斯:有困难喔,那不然就是三天吧…
过了一个礼拜,东西才写出来。
布鲁斯:奇怪喔,这个问题,怎么会要做这么久?我记得以前这个问题,我们大概都是一天就搞定了。
丹尼尔心想,是喔,是几百年以前啊?:系统变得比以前复杂很多啦。
对于系统有概念的主管,常常会忽略掉每个人的学习曲线应该会有所不同,也很容易就会忽略掉系统的演进,会带来复杂度的增加。更重要的是,因为他没有掌握系统所有演化的细节,当他在发号施令时,常常就会给一些不是那么正确的指示。而且还要求程式设计师要完全遵照他的指示去做。如果他不幸言中,还会跟你唠叨一些『我早就跟你讲过了。』『不听老人言,吃亏在眼前。』
过犹不及都不是好事。就因为每个人都会受到他的认知的影响,所以要不就是没有规划,要不就是规划的不符合实际需要。所以我们在维护系统时,考的都是临场的反应。反应的时间很短,自然造成边际效应的机率也会增高。边际效应一旦产生,你就会陷入解bug、没时间测试、赶快上新code、制造了新的bug…的循环。
当你落入这样的循环,这时文件的更新常常就是变成一件有空再做的工作。累积久了就会什么都没做到。这就会引出下一节的主题:『文件通常没有随之更新。久而久之,只剩下考古的价值。』
文件通常没有随之更新。久而久之,只剩下考古的价值。
关于在软体开发的过程里,到底要写什么样的文件,才是足够的,一直没有一个很好的标准答案。某种原因来说,也是因为每个人要做的系统不同,观点不同,也就一直有着不同的理论。
我曾经写过一篇文章,表达我个人对于Use Case driven的OOAD开发方式的看法。或许是因为没有特别强调,所以很多人认为我是坚决反对采用这种方式开发的人。
如果真的造成这样的误解,这真是冤枉大了。我怎么可能会放过这种向大师与流行趋势靠拢的大好机会呢?或许是因为我没有表达的很清楚,才会造成这样的误解。
我原本想表达的含意是如果使用Use Case driven的OOAD方式来开发跟资料库应用相关的程式的话,那么采用这种方法,我个人觉得并不像采用传统的结构化系统分析来得适合。
事实上如果你需要描述actor跟系统之间非常复杂而频繁的互动关系的话,采用Use Case driven OOAD,其实是很适合的。这世上还有很多系统,是跟资料库无关的,像是文书编辑软体,绘图软体,影像处理软体,网路通讯软体…,这些东西哪有什么data flow diagram?你不用use case来描述,我还真的会觉得你要不要去看一下医生哩。
主要是因为每个人做的系统特性不一样,就应该采用不同的方法来进行开发。我以为这就跟为什么男生的内裤应该开个洞,女生的内裤就不用这么麻烦一样,是相当浅显易懂的普通常识,不需要特别强调。
不过后来才发现,对于大多数人来说,这种推论并不是那么直觉。很多人做系统时,并没有那么多的开发经验。所以都是听公司里面的超人说说,就决定要遵循某一套开发流程,撰写某一种特定的文件。
接着采用一种方法,做过几个案子之后,就会有一定的看法。接着,常常就因为公司的长大或是自己的职位也往上爬了,就要开始参与『制订公司标准程序』的讨论。
对于很多主管来说,应该要建立一套可大可久的制度,并且要订定标准的作业程序,这样开发出来的软体,才会符合一种持续改善的水准。这听起来多么地言之成理啊。所以制订公司标准程序,就变成了公司里面的老手大拜拜。没有多久,就会被拱出来谈一次。
正因为通常参与讨论的人,都有不同的经验与背景,所以在蛮多公司都会有方法论的战争。简单的讲就是要写什么文件,要画什么图,只有我讲的才是对的,你们根本就太过天真,没做过真正的案子。订定标准是需要开战的。每个工程师对于要写什么文件,通常会有很枯燥乏味的辩论。越强调民主的公司,这种辩论就越冗长。
到最后,常常政治角力的结果,就是什么死人文件都写。有些公司还会定义出很多标准,所以到最后,就会变成是为了写文件而写文件,为了符合文件的标准,而写文件。对于大多数刚出社会,就踏入软体开发业的年轻朋友来说,其实他们刚开始工作时,都会想要照着规矩跟process,好好地写文件。如果这时后又接收到莫名其妙的指示,菜鸟们因为对于系统开发也不熟,就会一方面会写出奥妙难测,不知所云的东西,另一方面还认为遵循系统开发标准流程很重要。
对很多人来说,他们并不知道为什么要写这份文件,也不知道写了它应该要发生什么功用,只知道,这就是标准,这就是大师的金玉良言,这就是宇宙运转的定律。跟地球会绕着太阳转一样,是无庸置疑的。可是正因为他们不了解为什么要写这些文件,以及文件与文件之间应该有什么因果关联,所以就是虚应故事,为了写文件而写文件。这世上比这还无聊的事情应该也不多啦。
所以常常到后来,文件便不是开发软体时,辅助思考的产物,而是像小学生要学写字一样,得要照着笔划抄写的作业本。人菜的时候都比较好骗一点,所以菜鸟们通常都会写的很高兴。可是日子久了,就会发现,为写文件而写文件,其实是很浪费生命的一件事。
当他不了解一份文件到底是为何要写时,如果遇上一个很紧急的bug,第一件事当然是赶快把错掉的程式改到对。或是找出一个可以解决的临时方案。然而对于一套系统来说,问题其实常常是层出不穷的。每天在救火,久了之后,通常就没有力气去更新文件。
对很多程式设计师来说,维护系统时,该过着什么样的生活,选择只有两个。
A.赶快把bug解一解,接着就可以准时下班,陪女友聊天看电影,到山上海边看星星月亮与日出。文件管他去死。
B.乖乖写好文件后,再去解bug,因为加班到深夜,结果半夜回家时,还被喝醉酒的汽车撞到,在救护车上,女友还打电话来说你都冷落她要求分手。
想要活得幸福的人,通常都会选择正确答案,也就是A。如果因为没有follow标准流程,良心会有些许的不安的话,就会记得在拜拜时,多添点香油钱。
只要在维护系统的过程中,有一个人没有更新文件,接下去每个接手的人就会越来越没有办法来更新这些莫名其妙的文件。因为东西只会越来越乱。久而久之,文件就会变成只有专案考古学上的价值。所有的真理,就只会躺在程式设计师所写的原始码中。而原本有用的文件,就会因为年久失修,变成徒具参考价值的垃圾。
如果我们可以透过reverse engineering的方式,架构在程式的原始码上,让电脑可以自动解析原始码,并且架构在原始码上,重新更新现有的文件,可能所有的问题都会迎刃而解。不过在目前的开发方式上,这方面的开发工具,进行反向工程的结果,依然与人工进行分析设计有一段不小的差距。或许在不久的将来后,整个更新文件的工具会让反向工程的结果,更接近我们的智慧结晶。不过这个问题一直到现在,都依然没有一个比较有效的解决方案。
于是乎,每个人都说文件很重要,可是怎么样才能有效率地维护文件,让它能跟上系统的演化而更新,那就不得而知了。
我们现在面临的问题,某种程度来说,应该是开发工具可以解决的问题。如果我们用来做modeling的CASE(作者注:computer aided software engineering) tool与programming language之间可以做更完美的结合的话,那么文件就应该是在思考构思一个问题时的副产品,不是一个没人想去撰写的苦差事。不过单单就开发工具来说,截至目前为止,距离完美的目标,还有一大段路要走。这当然也会影响每家公司对于软体开发流程(software development process)的制订。
我个人是觉得,徒法不足以自行。到了现在这个年代,任何一个process,应该都要考虑跟开发工具要进行什么样的结合。没有使用适合开发工具的process,就没有办法在一家公司里真正地生根,接着就会很容易因为project在赶时间,而事急从权,变成一匹脱疆的野马。然后所有标准流程,以及文件的标准,就会被弃之如敝屣。等到专案告一段落了,其实也没有人会真正关心文件的生死。
这种事情在大公司,会专门找个人来稽核。在开发的过程中则会专门找个人来搅局。例如:
布鲁斯:我想我们分析开发这个阶段的deliverable,应该只要交activity diagram,class diagram,sequence diagram,use case specification 就好了吧?
凯莉:这个我比较不懂也,要问IT。
IT承办人吉米:我想如果照我们这个系统来说,因为我们采取OOAD的开发方式,大体上应该这样就够了。不过我要求你们把ER model也画出来,并且要做data dictionary。
布鲁斯:我们的database都会map到class diagram上面的class啊。这样会有很多extra的effort。
IT承办人吉米:不行啦,你们ER model还是要画啦。Data dictionary也是不可以省的啦。否则我会被DBA complain到死。
布鲁斯:ok。这个要求蛮合理的。所以就这样决定?
跨部门文件标准小组的成员:等一下。虽然我不懂这个系统在做什么,不过依据公司CTO在山上冥想,接收到的神谕而铭刻在最新出炉的软体开发十诫,石板version 所揭示。为了符合ISO 9000还有CMMI的标准,你们要订定并收集在这个阶段的软体品质资料,标准作业指引与程序,标准表单。还有要建立改善品质的专责机构…(这傢伙念了五分钟,以下略),因为品质是最重要的。还有就是公司的software architecture standard board开会决定,应该要在SA/SD phase撰写high level design / low level design document。所以要画data flow diagram,要写程式规格书…
布鲁斯想,靠,这傢伙是来乱的。看看吉米,他也一副莫可奈何的样子:可是我们走的是OOAD啊?
跨部门文件标准小组的成员:没办法。标准就是标准。吉米,不要忘了,在project告一段落之后,稽核会来做检查喔。
布鲁斯:可是我们走的是OOAD加RUP啊?
跨部门文件标准小组的成员:Software architecture standard board还在讨论OOAD加RUP的标准。不过他们有个草案啦。依照这个project的scope来看,最少也要有vision、requirement management plan,这样才能对requirement做详细的tracking…(这傢伙念了十分钟,以下略)…不过呢,这个还是个草案而已喔。所以等到标准定下来之后,里面真正的内容,还有可能会有出入喔。当然,如果你们要follow OOAD来开发的话,真正的deliverable还是要符合那个最新的规范。我想这就是我们跨部门文件标准小组目前的意见。
布鲁斯从瞌睡中惊醒:…讲完了喔。我们可不可以休息一下,去上一下洗手间?会议休息十分钟好了。(妈的,真衰,遇到一个来乱的。我应该去弄一个十字架?还是去准备几颗银子弹?) (作者注:吸血鬼怕十字架,狼人怕银子弹,这应该是看电影看多了就有的常识吧?)
感觉有点离题,不过还是总结一下我对于开发文件的看法好了。
***********************************************
1.每个开发团队,都应该要针对他手上要解决的问题,选择适当的开发文件以及开发流程(process)。不同类型的专案,就可能会有不同的需求,就应该要撰写不同的文件。基本上,这个决定会深深影响后面的开发作业。
在不会有负面影响的情况下,制作最少的文件,这是最高指导原则。毕竟我们要的是系统,不是去砍伐森林来制造文件。要达到这个原则,对于为什么要撰写这类型的文件,每个人都应该要有清楚的了解。
如果没办法达到共识,那就让官位最高的人做决定吧。不过着要的是,一旦决定了该写什么样的文件之后,project manager就要推动这个决定的执行。
2.相同类型的专案,应该要采取类似的流程,撰写相同类型的文件。这样会有助于经验的传承与累积。
3.决定了开发流程与文件后,寻找适合的开发工具。接着将开发工具的使用,订定出一套标准的做法,并且将这套标准的做法,视为process的一部分。
4.在专案进行的过程中,除非万不得已,不要轻易更动开发文件的种类或是主要的开发流程。这类型的变动,很有可能为专案本身带来不可预期的风险。
5.微调(fine-tune)开发流程与开发工具的结合,改善modeling、implementation、testing之间的效率。特别是当change发生时,如何让更动相关文件的effort降到最小,是个非常要紧的题目。
6.project告一段落之后,回过头来feedback到process与tool之间的改良上。
7.对于同类型的专案,整理出共用的pattern、document template以及framework。如果你做的专案都是属于同一种类型的话,这种经验上的累积与文件的适度标准化,将是公司非常重要的资产。
回归主题。结案之后的维护工作,为什么文件的更新会很头痛,主要再于要解决问题时,要修改的文件数量过多,时间太短,而人力过少。而当传承的过程里,一旦有人失职,文件就会跟系统不sync。最后就徒具参考的价值。
我个人认为这个问题,基本上是跟开发工具与开发流程有关。如果文件的更新与开发工具本身可以紧密结合,让文件变成是一个随时都会自动跟随着程式码的不同而保持更新的东西,那问题就解决一大半了。目前看到不少开发工具,确实也在朝这个方向前进。在那之前,就只能依赖人为的控管了。
不过,某些时候,文件的没有更新,是project team刻意操纵下的结果。这种可怕的阴谋论,就跟我们下一节的主题有关:『系统做的越烂,越有保障。做系统的人吃定客户找不到其他的人可以maintain。』
系统做的越烂,越有保障。做系统的人吃定客户找不到其他的人可以maintain。
很多人都会认为,我如果系统做得好,表现良好的话,这样下次还会有机会优先承揽客户的案子。
如果客户的案子源源不绝,你也有能力一直做新的案子的时候,这样可能还勉强可以说得通。不过对于很多软体公司来说,软体的维护合约才是他们最重视的一块肥肉。系统做的不够好,我才有贩卖新版,跟收取维护费用的机会。
关于如何才可以做出一个卖得出去的烂货,这是行销学与资讯工程学两门学问紧密结合之后的结果。因为我也不是这方面的专家,就很难有精辟的见解。不过烂货卖出去之后再来卖升级版,卖维护服务,就可以赚不少钱,这并不需要在这两门学问中有任何钻研,就可以很容易的推论出来。事实上,如果系统做得越烂,可是客户又每天都要使用,那客户就等于是上贼船一样,任你予取予求。
这种转变并非来自一夜之间,不过在上线之后,它就真的会发生了。
凯莉:布鲁斯,布鲁斯,救命啊,我们的系统当掉了。
布鲁斯:你是谁啊?我怎么忽然得了失忆症?
凯莉:不要闹了啦,现在我们紧急把server停下来,系统已经有几千笔错误的资料了。赶快来帮我们看一看。
布鲁斯:咦,我们的保固期间已经到期啦。维护合约也没签,我们没有立场去帮你们做这个啊。我在想你们是不是要找其他的vendor去maintain啊?欢迎啊。况且我们现在很忙。
凯莉:哎呀,维护合约我们可以再谈嘛。先来帮帮忙嘛。
布鲁斯:那你先跟我们家的sales谈好了以后,再请他来找我。我可以先派个工程师去看看状况是什么。
凯莉:哎呀,你的大恩大德,我一定铭记在心。赶快来喔。
布鲁斯:我还没有答应要修喔。只是先去看看状况。不要忘了维护合约啊。
凯莉:没问题,凭我们两个的交情,这一定是最速件处理。
布鲁斯想,那三个月前叫你签维护合约时,怎么就不这么爽快啊?不过既然客户上门,现在也不为己甚:好,我会请工程师尽快跟你连络。
系统做得烂,可以确定的是,不会有太多人想要去碰这个烫手山芋。如果文件既不齐又过时,那你简直就是有一张长期的饭票了。在客户还没有把这套系统全部换掉之前,你就等于在客户端那里架了一台印钞机。先前开发时,不管在客户端那里受了多少鸟气,此时都可以一口气要回来。因为这时候你只要耍性子,说一句『老子不干了。』马上会有很多人流汗。通常高阶主管会赶快过来关切,找你喝喝下午茶。
『这个…艾力克君啊,最近是不是有什么照顾不周的地方啊?是你的PM太机车是吧?没问题,我把你换个部门。要不要去休个假?好好调养一下身心啊?公司很需要你这样的人才啊。』(妈的,你这个死孩子,要不是公司找不到其他人来接这个烂案子,哪由得你这么猖狂。)
我总觉得,在开发专案的过程里,其实每个成员都扮演很重要的角色。如果你把他惹毛了,他只要一作怪,你就会吃不完兜着走。可是我所接触过的不管是客户也好,主管也好,好像从来都没有警觉到这一点。对于team member通常都没有足够的尊重。平时大呼小叫,妄加斥责,或是不闻不问,毫不关心,可是一旦有紧急事故发生,马上就嘘寒问暖,装熟请你喝咖啡,要你大力帮忙。其实在关键的时刻,一个人并不需要故意做伤害你的事情,他只要袖手旁观,或是故意保留实力,你就会流眼泪了。
客户面对这种状况,难道什么武器都没有?当然不是。客户最有力的武器就是改版。各方角力的结果,产生的现象,就是:『大多数做专案的公司,低估了保固期间的effort。取而代之的,是持续不断的改版。』
大多数做专案的公司,低估了保固期间的effort。取而代之的,是持续不断的改版。
在什么样的前提下,程式设计师才有办法耍大牌?答案是
1.这套系统没人想接手
2.客户非常倚赖它
3.只有你一个人,对于怎么维护这套系统才有概念。
这三个状况都成立的情况下,程式设计师会受到贵宾一般的礼遇。
拿好莱坞的电影来说,这种情况等于就是所有的人,都认为这台在高空中飞翔的空军一号专机上,被恐怖份子装满了随时会爆炸的炸弹。而美国总统这时也坐在上面,而降落伞却又被恐怖份子给全数破坏了。这时候,你是飞机上唯一懂得如何拆除炸弹的特勤小组成员。
在这种情况下,是不是真的有炸弹,只有你才知道。你要花多久的时间才可以把炸弹拆掉,这也只有你才知道。所以程式设计师如果遇到一个做的很烂的系统,日子其实可以过得轻松愉快。
因为做得很烂,所以没什么人会有来接手的念头;因为只有你会,所以你说这个bug要修一个月,一个月内铁定看不到成果;因为只有你知道炸弹在哪里,所以你可以编造出很多虚拟的炸弹出来。就我们的专业术语来说,就是:『我觉得某某模组还有一些潜在的问题,可能需要好好地去重新check所有的code。不然要是哪天出问题的话,大家就完了。(这时请记得把双手一摊。做出个无可奈何的表情。)这件事,在我没有好好trace这段程式之前,我一点idea也没有。所以我想我可能要花一个月的时间来仔细trace这段程式。如果没问题的话,我们两个人,就一个月之后再见。』
如果你在看电影,什么时候这个特勤小组的成员会变成只是个三流的配角来演?根据资深影评独孤木先生(咦,刚好就是我)表示,有下列几种可能:
1.主角是从另外一台飞机拉一条线,爬进空军一号的爆破专家。也就是说,有人找了个很强的外来和尚来念经。(通常是什么顾问啦,还是某公司的强人那一类的。)这个人用他的X光眼睛一看,就看穿你是纯粹在混时间等养老的工程师。而你声称没有办法解决的问题,大概一个会写程式的小学生就可以解决了。
2.主角就是美国总统。他自己从飞机上跳出去,从数百公尺的高空跳机之后,刚好遇到跳伞专家从他头上飞过来把他抱住,然后两个人用一套降落伞就愉快地降落在充满比基尼美女的热带沙滩上。也就是说,客户决定换一套新的系统了。这时候你就一点筹码都没有,只能眼睁睁地看着你的优势瞬间消逝。当总统先生已经跑去沙滩上面,跟美女搂搂抱抱玩亲亲的时候,你还坐在这台飞机上面拆炸弹。
3.这炸弹真的爆了,结果发现威力跟个仙女棒差不多。这就跟我们恐吓客户说,你要是不付钱给我的话,到了公元两千年时,这套系统就会爆炸起火燃烧,化为一团灰烬。可是过了Y2K,你发现这系统连屁也没放一个是一样地没面子。(当然,你可以说,东西没爆算你运气好。也是因为我们先前做的系统非常robust。如果你还有收到钱的话,这时当然可以大大地邀功一番。声称这个可怕的问题,就是在你们team member无日无夜的努力工作之下,才会化解于无形,只留下高额的帐单令人悼念。)
很多软体公司在开发系统时,都会同意提供半年,或是一年的免费保固。可是很多开发系统的厂商,对于保固期间要花多少人力跟经费,根本就没有良好的planning。很多高阶主管都认为,货物一旦送出门,就会自动地变成defect free。所以每位专案成员,只要在专案结束之后,行程早就被主管们排到满档。维护系统这种小事呢,只要找大家有空的时候兼着做就成了。
问题是事情并不是如此简单。保固期间如果一出事,常常要花很大的人力、时间与精力去善后。
对很多高阶主管来说,保固期间所要花的人力物力,都是意料之外的开支。所以不想办法从客户那里多搾一点油水出来,这样子哪行?对于客户来说,发现所有的款项都付光之后,就遇到叫天天不应,叫地地不灵的客服语音询答专线,不拿点红萝卜出来引诱一下厂商,这也是不成的。
大家都有了共识之后,就会开始规划改版。如果厂商的配合度还不错,这时规划的就是个小改版。直接找你谈要改的东西,虽然金额不一定很小,不过一定会避免找其他的厂商进来;可是如果厂商的配合度不够好,就会威胁你,我要找其他的vendor进来喔,看在将来会有一大笔钱的份上,你要不要现在免费帮我改这些现有系统的bug?我可能还会考虑再把这个新的案子给你做喔。接着当你为他做了很多免费的service之后,他还会坐在他的主管专用大皮椅上,翘着二郎腿,趾高气扬地考虑要不要把生意给你做。
正因为大家对于这种奇怪的现象没有什么比较好的方法可以解决,而你不付钱就买不到堪用的系统与服务时,持续的改版就是一种常态。如果你不改版,其实软体公司还有釜底抽薪的一招:『我们对于version 1.0,从下个月开始就不再继续提供相关的支援服务了。旧客户们,请记得一定要upgrade到新版v 2.0,不然你们就会变成是软体界的孤儿。别人用新版做出来的档案,你们就永远也读不到。』
依我的观察来看,大多数的改版,其实并没有产生那么大的效益。不过也就是因为有这么多层出不穷的改版,软体工业也才会有这么多新的活水可以溢注。以一个从业人员来说,我倒也不排斥这种现象啦。况且除了持续不断地upgrade,好像也没有什么比较好的solution。
***********************************************
讨论了很多的现象,其实我自己对于很多现象也都没有答案。不过我个人始终相信,成功地定义并描述出你要解决的问题,是找出解答的第一步。要是你说不出来问题是什么,你就算与解答擦身而过,也一定是视而不见吧。
在这一章的结论里,我想谈谈一些心理层面的观点。
把一个难缠的案子结掉,不是什么光宗耀祖的大事,不过对于在资讯业界工作的人来说,一定就会有不少可以吹嘘传颂的故事。最少当你在吹嘘你们工作是多么地暗无天日,艰苦卓绝之时,只要不是遇到同样在这个team的member,就不会有人来戳破你的牛皮。
我回想了一下在自己手上结掉的案子。有几个做的很惨的案子,在结案之后,我都会衷心期待可以赶快度过保固期,然后从此不要再见了。不管是enhancement还是整个改写,我都不抱持着太大的兴趣。当你每天都在救火时,你不会有时间好好去思考,可是当你救完火之后,那种内心深层所涌出的厌倦,就会促使你竭尽所能地去避免自己再跟这个案子有任何的瓜葛。
也有几个被腰斩的案子,我常常都会想,这些案子如果可以继续做下去,结果会变成怎么样?
不过最令人兴奋的,就是做到一些很有趣的案子。除了自己会觉得很有成就感以外,你还会不断地思考,要怎么样去改善,才可以让它更符合你的期望。
当我开始在构思并撰写这一章时,刚好才刚结束一个长时间艰苦奋斗的专案。那时候,每天想的就是怎么样可以把这个烫手山芋结案。结案之后,还有多少无可奈何的事情要面对。
这阵子我刚好自己出来创业,自己也规划了几个有趣的产品,希望可以提供市场上所不曾见过的服务。这个时候,每天在想的事情,就是怎么样可以把这个东西做到更好。所有的team member,也都保持了高度的士气。这种生活,是以前从来都没有想过的。在下结论的现在,则刚好是头一个产品的beta测试期间。
心态的不同,会让你对相同的事情,有着截然不同的观感。
当你做的产品,能够符合人们的需求,当你做的产品,可以让你觉得你确实创造了不同的价值,这样的工作,会让你充满成就感,这个时候开发软体,就会是人生一大乐事。否则的话,就会是一桩苦差事了。
如果你结案之后,没有找到属于你的兴奋与快乐,那么可能要换个案子,换家公司,很有可能你跟她同居了三年,到了要结婚之前,才发现她不是你的Miss right。拋弃同居女友可能不是一个明智的选择,不过换一家工作时可以给你带来成就感的公司,应该是一件很好的事情。
***********************************************
因为新工作的忙碌,所以这一章写得断断续续的,没有多少东西,可是前前后后写了四个多月,前后文可能有些不太连贯。这是要向诸位读者朋友抱歉的地方。
阅读(516) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。}

我要回帖

更多关于 高铁票丢了还能退吗 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信