一年前的噩梦–记十年职业生涯最黑暗的项目经历


做我们这一行有一定年数的,如果谁敢说他参与的项目从来没有失败过,那他显然是在吹牛。但是在每个人或多或少闪着光芒的简历上却很少看到对项目失败的标注。在这篇短短的博文里,我将记下一年前我经历的最黑暗的项目开发经历,并希望借此寻找一下自己能够从中反思的东西。

而立之年
大约一年多前,我结束了为期三年的项目,同时也离开了为之工作了三年的公司,试图在30岁到来之后寻找新的挑战。通过职业中介,我找到了一家不到100人的小型公司,业务内容是我熟悉的汽车导航设备的开发。

这是一个管理层至上式的传统风格公司,尤以老板的发言为真理。同时无论是业务部还是开发部乃至测试部,都一律要求必须穿正装打领带出勤。对上班时间的要求也极为严格,早晨哪怕迟到一分钟也被看待为整个上午缺席。虽然我很不喜欢这些束缚,但是为了尽早开始下一个挑战,同时就业的严峻形势也摆在眼前,我接受了他们的招聘邀请,从3月开始了新的工作。公司分为神户总部和东京分布,由于现实因素的原因,我要求加入的是东京分部。

令人困惑的第一项任务
由于我的英语能力还凑合,新工作开始后第一份任务是进行API文档的英译,以便提交给某公益开发组织进行认证。但是说到翻译,其实却根本没有原稿。任务是当时的上司FU颁布下来的,他提到API的说明都内嵌在代码的注释中,只需用JavaDoc便能导出,然后在HTML的基础上翻译即可。听起来挺简单的,于是我便开始了这第一份任务。
但是随着工作的展开,我发现了几个明显的问题:

  • 首先,并不是所有的API都标注了合适的注释,有些只有简短的语句,有一些参数变量在API内完全没有被调用,有些甚至根本没有注释。
  • 然后,整个项目没有任何机能说明书,基本设计书,详细设计书和测试用例。
    我尝试着去运行程序并理解API的调用机制,却意识到在我手里的只是一部分被调用的库代码,整个主程序却并没有提供给我,也就是说没有任何程序调用这些API。

此时,我尝试着和FU沟通,以期能够得到调用这些库的主程序代码。但他却几乎一直在出差,偶尔抽出时间和我交谈的话,对主程序也只是闪烁其词,避重就轻。不得已,我只能从未知的API所调用的内部函数开始人工跟踪,试图找出这些API的作用和参数变量的用途。就这样我勉强在规定时间内完成了任务。
当我把翻译结果提交给F的时候,心里其实非常担心。因为关于我翻译的内容,仅基于我一知半解的理解,自己都非常没有把握。但是FU却几乎没有细看,似乎仅扫了一眼翻译后的产出量便说:好,那么我们差不多该谈谈下一个项目吧。
我其实挺惊讶与他当时的反馈,心里也有一些想提的问题,但是基于既然上司都没说什么我何必自找麻烦的心理,我沉默了。

噩梦项目的开始
关于第二份任务,FU对我做了简短的介绍,大致就是根据客户的要求把我们公司内部以前开发完毕的一整套导航系统移植到客户指定的开发板上。看来终于可以接触到一个完整的导航器了,但是移植前的平台和移植后的平台到底有多大差异呢?这些都还不明白的情况下,我只能带着半分激情和半分忧虑开始从客户那边收集需求。而噩梦也就从此开始了。

我走访了客户的公司,从访谈中试图了解他们的需求:他们需要在一个指定的开发板上使用一种名为T-Kernel的OS,这是iTron的一种升级版,对多任务提供了线程和进程两种方式。由于最终成品需要和客户开发的程序共享一个三核的ARM v7型CPU,所以必须采用进程多任务的执行方式。至于导航系统本身的功能,项目负责人也就是上司FU给他们展示了一个公司曾经开发完毕的掌上型原型机。客户的需求很简单,只要像一个导航系统就行,顺带加了几项还算容易实现的机能。不过后来的事实证明了,其实越是描述简单的需求,其中暗藏的陷阱却越多。

回到公司FU和我的话题回到关于应该采用公司的哪个前期产品作为移植对象。FU提到了两个选项。

  • 一个是公司总部正在为某掌上游戏机公司开发的一套即将完成的导航产品。
    由于其中加入了一些寻路算法的优化,API也做了一些变更能够更灵活的适用这次客户的新需求。但是问题在于,当初是为游戏机的低速CPU开发的,为了实现流畅度关闭了很多游戏机上不必要的底层代码和机能代码。如果移植到客户提供的硬件系统上并且重新打开关闭的代块码的话,流畅度很值得怀疑。而且致命的是,这套系统虽说已经接近验收阶段,但是根本还没接受内部SQA小组的检验,无法知道其中有多少Bug。
  • 另一个即是当初提供给客户的原型机使用的代码,原始开发人员也是公司总部的人。
    由于代码有一些年份,最新的一些机能没有加入。想要加入对新机能的实现需要花费较大的精力。但是硬件相似度却很接近开发目标的硬件,因此软件的性能应该是能够得到保证的。

由于对新硬件和新OS的了解非常有限,我主张采用第二种比较稳妥的方案,即以成熟的方案为基础,先在新平台上实现基本的导航系统,然后再考虑功能的扩张。但是FU却不这么认为,他提到基于第二种方案的导航平台,总部已经停止维护。唯有采用第一套游戏机导航系统的方案才有可能实现客户要求的功能上的追加需求,哪怕必须为此冒上可观的移植风险。
无论是第一方案还是第二方案,就像我之前的翻译工作一样,没有任何设计文档可供参考。
由于他是我的上司,且我刚刚来到公司,对待移植的导航系统也缺乏了解,加上东京分部的开发部长SE轻描淡写得说“这样的任务三个月拿下应该毫无难度”,我想我还是保持沉默并且先谦虚得学习公司的代码和开发方式吧。

三个臭皮匠组成的杂牌军
很快,开发队伍被组建起来了。除了我,另外还有两人。

一位是和我几乎同期加入公司的年轻人,在这里我先称呼他HAN吧。他对嵌入式开发的经验几乎是零,自然我们也不能指望他有任何导航器开发方面的经验。能够依靠的只是在Windows平台上的C#开发经历。由于总部的导航器提供了一套在Windows上运行的模拟环境,而这套环境是使用C#编码的,所以对新导航器UI的调整工作就交给他了。

另外一位是外包公司的TAKADA,他对iTron的底层调用非常熟悉,他被分配的任务是修改游戏机导航系统上的OS底层调用接口,使它们兼容T-Kernel上对应的底层调用。而我则负责编写驱动程序接口,交由客户去开发各个硬件模块的驱动程序,包括触摸屏/GPS/Gyao/NandFlash/Graphic/Sound,在拿到驱动程序后,我必须把它们和导航系统内部的调用接口挂钩完成系统对硬件资源的调用。另外,客户要求最终系统必须是以进程方式运行,而针对进程间通讯,公司的开发经验也是零。所以一开始我们决定先实现线程多任务,等差不多实现功能后再跳转到进程多任务方式。这项工作将交由熟悉OS的TAKADA去完成,当然我也会分担一些实现进程间通讯之类的任务。

更重要的一点是,从我们的计划表上看根本没有进行单元测试和代码评审的时间和空间。当问及我们怎么做测试的时候,得到的回答竟然是,你们把所有的功能都实现了,整体交给神户SQA做结合测试即可。至于开发中途,对不起没有测试资源。

半残的移植原型
FU作为项目经理,分配完任务就出差去接下一个任务去了。此后两周再也没有在办公室见到他的踪影。TAKADA在埋头看他的T-Kernel文档,我和他聊了几次觉得他那边问题应该不大。毕竟T-Kernel是iTron的升级版本,很多系统调用都非常接近,甚至名字也只是改一个前缀而已。他和我都觉得在原定的Milestone之前完成底层移植没有压力。而我自己在看完了系统内部对硬件模块的调用方式之后,编写了驱动程序接口的设计书并交给客户,等待他们的开发。

但是这段时间我却始终留意到,HAN在一刻不停得转他的笔。笔在他的指尖上开始旋转,然后就是啪嗒一声掉在桌上;拿起,继续旋转,啪嗒。。。这样的过程他可以从上午9点一直持续到晚上9点。是的,坐在这个办公室的人不可能不意识到他这个让人讨厌的习惯。而我更在意的是:他是不是被卡住了?于是我们找了个机会谈了一下:

“嘿,HAN,模拟器那边怎么样,一切看起来还行吗?”
“不行,问题很多。表面上你虽然看不出什么,但是这个系统根本不能搜索路径。地图上的一些标示也显示得不正确。”
“怎么回事?他们不是说这个系统快接近完成了吗?”
“扯蛋!你看很多功能按键其实都只是个摆设,你可以按下去,但是什么都不会发生。”看来HAN已经有些不耐烦了。
“别急,我想时间还有一些,我们一起来看看代码。”
“好吧,希望能看出点东西。”

于是乘着还算空闲的间隙,我和HAN一起梳理了一下需要解决的问题,把它们列出来并标注了优先级。
然后我和HAN各自领取了最高优先级的几个问题,开始看了起来。
几天后我搞明白了我负责的部分并且解决了一些问题。
代码中通用的毛病我也领教了个大概:

  • 一些API调用内部其实是空的(源项目开发还远远没有完成)
  • 一些API调用的传入参数和内部使用的逻辑完全不符
    (由于进行了所谓的API优化,内部逻辑的变化造成对参数使用方式的变化。但是API开发组和API调用者之间似乎还未完成相互的确认。源项目在到我们手上之前只完成了最起码的要求:编译通过!)
  • 很多地方使用直接数字作为常数,且没有注释。
  • 不仅如此,针对功能模块的逻辑,没有注释也没有任何外部文档。
    (干过之前的翻译工作后,我对这种现状丝毫不感惊讶)
  • 为了适应游戏机的低级别硬件,导航系统内部移除了较多的功能,但是没有任何文档提到他们移除了什么以及他们是怎么干的。

也就是说现在在我们手上的这套待移植项目充其量只能算个半成品。我们还必须承担起排错并且完善它的机能的任务。我立刻意识到我们几乎不可能独立去完成这份额外的工作。
这时我想起了原型机的代码。我们可以通过比较得出有多少功能被屏蔽,以及怎样找回被屏蔽的功能。

HAN也攻克了他手上的几个难点,同时他得出的结论也与我大致相当。我们一致认为,如果确实存在任何可能有关的导航系统设计文档的话,那正是我们现在急需的。我们再一次向FU提出了这个要求,他答应帮我们找找,但是在那之后就再也没有任何回馈了。

项目不等人,我和HAN痛苦得比较着两个项目的代码,寻找它们机能上的差异,从代码层面理解新旧两套API的实现,并最终试着把一个又一个未实现的或是被人为移除的机能找了回来。看着我们最初列下的机能缺失表上一个又一个目标被渐渐划去,模拟器上的导航系统看起来已经越来越接近真实的系统。项目的计划时间已经过去了三分之二,而真正的移植工作却还未开始!!

马上,底层调用的大部分代码和客户提供的驱动程序都完成了,我们开始了正式的移植。但是由于客户的经费问题,我们只能依靠唯一的一台开发机继续我们的工作。TAKADA需要它来继续他接下来的底层调用的调整,而我也需要它来实现连接驱动程序和内部调用的工作。由于TAKADA属于外包公司且只能白天工作,唯一可能的选项就是我从那天开始上夜班。理论上是晚上9点到第二天早晨9点。但是由于需要进行工作的交接,FU尝试着说服我留到第二天上午10点,鉴于摆在眼前的困难,我也同意了。当然,后来的情况证实了如果我能在上午11点前离开公司赶着回家睡一觉已经是谢天谢地了。另外,为了防止可能发生的不测,公司把HAN也分配到夜班与我做伴。

在接下来的一周内,我用最快的速度完成了各个驱动模块的组合和测试。HAN则继续他在模拟器上的调试工作,试图在移植整个导航系统前清理更多的问题。一周后我完成了除Graphic以外的所有部分:GPS已经能够定位,Gyao能够辨别方向,声音能够从喇叭里听到,触摸屏幕也能够得到相应的坐标,等等。而Graphic由于涉及到了较多的底层调用,由TAKADA负责完成。TAKADA在折腾了一整周之后也给我们带来了第一个16位色的导航器界面。

遭遇严重的性能瓶颈
正当整个小组打算稍微松一口气的时候,我们遗憾得发现它的执行效率出人意料的低下。当点击地图上任一点的时候,几乎要等待4-5秒才能出现弹出式菜单,就算是固定菜单的响应速度也是同样得慢。完全不能和我们提供给客户的原型机做比较。我首先分析了我的触摸屏响应模块,通过调试证明几乎是触碰屏幕的同时,坐标就被传递到了系统内部。触摸屏是没问题的,那么问题只能出在Graphic方面了。

我和TAKADA使用开发商提供的IDE尝试对地图渲染和界面渲染进行测速,但是同样因为没有文档,找到并且区分这些渲染算法的代码也花费了我们相当的时间。在我们基本确认地图渲染方面有严重速度障碍之时,我们用完了我们向客户承诺的三个月交货期。

这时,FU已经扛不住了,他开始寻找更多出差的理由以回避管理层的质疑。而之前放话三个月拿下项目的东京开发部部长SE不得不和开发组一起去面对客户的怒火。

经过几轮谈判,我们试图说服了客户相信这套硬件在图形渲染上有先天的硬伤。即我们提供给客户的原型机带有硬件图形加速模块,而客户提供的开发板却没有。我们唯有用CPU去实现图形渲染。客户答应放宽交货期限两个月,另外让出一套他们自己使用的开发板,帮助我们联系硬件和IDE的开发商为我们提供技术支持。

凭良心说,这是我第一次遇到如此善解人意的客户了。我其实真的很感激并且很想告诉他们这些风险很早的阶段就应该能预见,但是公司的技术管理层却选择了无视,这些话我当然只能留在心里。

你敢下班试试?
此时恰逢公司从神户总部来了一个股东兼人事总监,一个肥头大耳的家伙。项目糟糕的情况似乎传到了他的耳朵里并且对东京分部的开发部长施加了压力。

第二天开发部长SE扳着脸把我和HAN叫到会议室,宣布公司现在开始对这个项目进行紧急调整:

  • 项目经理从FU换成SA
  • 另追加TAO进入项目组
  • 同时从现在开始,没有公司允许,不得下班!

正当我还在怀疑自己的耳朵的时候,他又来了一句,如果你们自以为公司如果倒闭而不用承担责任的话,可以试着下班!

这是威胁?还是责难?难道我回到了某种强制劳动的集中营?!

当时选择继续留在公司工作的我,没有一丝迫于这种胁迫式语言的原因,完全是出于为项目负责到底,并且对得起自己拿的那份工资而已。同时也因为项目中来还算有些挑战性的课题以及十年程序员的一些小小的但是不容侵犯的自豪感。

TAO也是一位刚来公司一年多的新人,同样没有足够的嵌入式开发经验。SA是他的直属上司,一个整天愁眉苦脸的家伙。我们召开了小组重组后的第一次会议,主题当然是怎样提高软件的执行效率。我们制订了几个方案,包括把地图上图标的色位改成8位;合并若干个图层虽然这会降低信息表示的灵活性,以及限制今后功能的追加;还有一招是把地图信息从SD-Card读取到内部NAND Flash上再实时调用。

我们各自被分配了任务开始了工作,同时我们被要求离开自己的办公座位直接抱着电脑搬迁到一个小型会议室中。肩并肩,面对面,鼻子顶着眉毛得干活。

据说这是公司最常用的施压方式。
这个“常用”,包含了项目出现窘境时的唯一对策和项目出现窘境的频率,这两层含义。

就这样,一周内我们平均每24小时要工作18小时,6小时用来睡眠。没有洗澡,没有替换的衣服,被征用的会议室桌子上散布着吃空的零食包装袋,地板的角落里则堆满了干瘪的可乐罐。整个房间里弥漫着一股酸味和汗臭,要知道这可是初现暑气的六月末。

我们有一些成果,延迟从4-5秒缩短到1-2秒,同时付出了降低色彩深度的代价。谁也说不准客户是否会接受这样的结果,因为和原型机相比差距还是显著的。我们再一次尝试寻找原型机的开发文档,结果不出所料得发现根本就没有。一切都留在当时开发人员的脑子里,甚至一些人曾经来自外包公司,人们很难去理解一坨充斥着数字和多层嵌套互相调用的函数是出于什么逻辑。

加班的另一种目的
不止一次,我们私底下在怀疑这种连续作战的意义。我们的思维已经迟钝,我们的语言也变得笨拙。打开电脑盯着代码看一整个白天再加一整个夜晚已经成为了习惯,但是效率却是鬼知道。直到有一次我无意中听到SA说,我们这样没日没夜得做其实也是为了做给客户看,以证明我们已经在努力了。

临近周末的晚上,我再也不能忍受身上散发着的酸臭,半开玩笑似得和项目经理SA说,我们晚上去找个公共浴室洗个澡怎么样。我很确定他听到了我的话,但是没有明确的回复。
我天真得以为作为上级的他不好直接表示同意,但是可能也体谅到包括他自己正在忍受的不适,默许了我的请求。

到了半夜12点,在出发前我还招呼了一下SA,但是依然是没有回应。于是我和HAN一起走出公司花了一小时在附近的公共浴室好好洗了一把。回到办公室后,我们继续工作了一会儿,然后带着久违的舒适享受我们那宝贵的6小时睡眠去了。

可是,第二天一早我和HAN就被叫到了另一间会议室,东京分部的部长YAMAUCHI正虎着脸在里面等着我们。劈头盖脸就开始责问我们昨晚12点到什么地方去了,在得到我的答案后,立刻就开始咆哮:

“你们知道这是多严重的错误吗?!”
“你们眼里还有没有这个公司?!”
“你们俩在我心里的评价从今天开始降低到零,零点!!不管你们以前为公司做过什么!!”

这一刻我感到我俩就像被抓获的越狱犯或者是奴隶主手下的农奴。我也意识到SA表面上不动声色看似默许,但是在我们前脚离开办公室之时已经迫不及待得开始告了密。是的,项目临近失败,我们需要一个罪人。我不知道HAN是怎么想的,但是我体验到了什么叫被羞辱和被出卖,做人最低的需求直接被公司歇斯底里的利益至上主义践踏在了脚底,以及对我自己天真得相信别人而产生的无限自责。

当时借助于必须保持职业人基本涵养的自我提醒,努力克制了情绪的爆发,即使我知道我的脸色非常不好看。从走出那间会议室的那一刻,我很清楚的一点是,我最后的底线已经被轻易突破,到目前为止我的努力都是在自欺欺人。在这里任何一个上司都不会在你有需要时体谅,宽容和帮助你,相反在出现危机时却把你早早得供出去以求自保。不管你曾经做过什么,只要谁能够被用来当作替罪羊,拿来用即是。

我,和这家公司的缘分是走到尽头了。

和老婆打了个电话,简单说了一下发生的事,并告诉她今天下班时间一到就会回家。
第二天我带着辞职信,直接放在了东京分部部长YAMAUCHI的桌上。

从递上辞职信到正式离职,还有一个月的时间。
这期间我力所能及的把自己完成的部分整理出基本设计文档和详细设计文档,并且标注了需要特别注意的地方。同时,毫不稀奇得发现,公司没有任何存放公共设计文档的途径,唯一能做的就是通过邮件直接发给项目经理。

后记与回顾
从进入这个公司到决定辞职,只花了我四个月时间,是我职业生涯中最短的经历,也是我历次辞职过程中毫不需要犹豫的惟一一次。

一般来说,对过去的事没有假设,但是自己的脑海里却时不时冒出各种如果。

如果第一任项目经理能够对进度再多关心一下,协调并提供一些必要资源的话。
如果第二任项目经理能够对成员的长时间承受的劳累和压力多一些体谅的话。
如果公司能够对员工的私人时间和基本权力的尊重抬到最基本的高度的话。
如果我能够更及时得提出对开发进程和前景不透明的预警。
如果我能够在强调自己的观点时,再那么坚持己见一下。
如果团队中的任何人能够在某些关头发出真实的声音的话。
如果我是项目经理,并且能够在必要的时候调用必须的资源的话。

也许,结果就会稍有一些不同了。

在我离开公司一个月后,TAKADA终止了他和公司的外包契约;几个月后项目经理FU据说也离开了公司。大约一年后,HAN和TAO也离开了那个地方。
从HAN那里听说,由于业绩不良,开发部部长SE之后要求所有员工填写一份关于自主上缴部分年终奖的不平等协议。这一举措激怒了另一部分人,导致了一次较大的人事变动。
作为曾经的一员,我对管理层能做出这样的决定毫不惊讶。

距离这段经历发生,不知不觉已经一年了。对比现在的工作,把那段经历形容为噩梦也毫不夸张。但既然是噩梦,又为什么特意把它记下来呢?

李笑来老师在他的《把时间当作朋友》中提到过,人的大脑倾向于忘记痛苦。而痛苦除了用来折磨人,还能帮人惊醒,助人成长。我记下痛苦既是为了不要忘记痛苦,同时也是记下我的这段成长的过程。希望某一天再来回首的时候可以提醒我,一个糟糕的团队和管理会怎样一步步把人拖入深渊,而自作聪明的沉默不语又曾经是如何助纣为虐的。

Tagged on:

3 thoughts on “一年前的噩梦–记十年职业生涯最黑暗的项目经历

  1. Williams.Lu

    虽然上司对我不太仁义,但是即使离开公司,也做好自己本分的工作,不作小人报复,也不作声色,也证明我现在做的是对的,我也是提出辞职了,公司对我没有挽留(主要还是上司)但是我还是做足欠缺的文档说明以及规划方案,以便后任上手!

  2. weijing

    深深共鸣!
    烂项目的过程是何等相似!
    此文转了~!

  3. weijing

    已转走,bulo.hujiang.com/app/diary/336134/
    作为在日工作的同行,我也经历过类似黑暗的项目,持续一年没有周末没有自由,连着干30个小时也是常有的事,偶尔10点半就下班了会高兴得不行,为了给节省打车费,公司租了个ウィークリーマンション,一群汉子打地铺睡在一个小房间里,各种气味至今难忘。项目的过程极其类似,一句话概括,前期该做的事情没做,到头来迟早会还。

发表评论

电子邮件地址不会被公开。