效率 vs 稳定 vs 后期维护 vs 成长
(为什么员工要加班)
----------------------------------------------------------------------------------------------------------------------
如有转载,必需保证本篇文章的完整性,包括本段内容。如果引用,必需注明文章标题、作者和时间。
黄延冬 于 深圳
2008.11.1
----------------------------------------------------------------------------------------------------------------------
目录:
1. 为什么写这篇文章
2. 背景
3. 问题
4. 标准化的漫长之路
5. 这就是我想说的
======================================================================
1.为什么写这篇文章
为了我自己减少工作,减少加班,特写此文。
2. 背景
经过几年的工作,终于发现,一个公司的成长,终究是经历着千辛万苦的。
从毕业之后的第一个公司,到之前的一个公司,三家,要么已经死掉了,要么缩小了,要么折腾来折腾去地没钱赚,看上去,各有各的原因,但结果却只有一个:公司的成长总是处于挣扎之中,好像没有成熟的时候,永远是“创业”阶段。
刚毕业的时候,在公司里能够写一个软件上能用到的代码就满足了,不管是一个窗口还是一个过程,不需要做任何考虑,写出来只要能正常运行就算是测试通过。后来换了公司,我志向于VC++的开发,不再使用delphi、vb这样的开发工具了,而VC++的上手,却经历了自我学习的3到4年时间,这个过程太过漫长,幸运的是,结果还算是用上了VC++,那时很高兴,觉得学习了一些东西,终于迈入了VC++的大门。
事实上,真正拿VC开始写较为广泛的程序,是从本公司开始的,以前,只是用VC做数据库开发,因为是公司内部使用的工具,模式都很固定,界面也不需要复杂和花哨,唯一要做的就是数据库的查询、写入、更新和删除这样的操作,所以,除了数据库的查询之外,对我并没有太大的帮助,每天也只是做着模式一致的几乎重复的工作。而现在却不一样了,有很多工作要做,有很多程序要写,有很多设备要支持,有很多应用要实现,有很多版本要兼容,有很多的程序架构要考虑。自从腾工离开之后,我开始思考这些问题,这些不是主动开始思考的,而是被动地开始思考。因为没有人指导,全是凭着经验,碰到了多次相同的问题之后,才开始变“聪明”的,才开始思考这些问题,我想,这已经够被动了,我们还要继续被动下去吗?
3. 问题
有两个问题:一个是当前的工作,一个是未来的工作。
实际上,这两个问题,已经被广泛地讨论过了,只是每个公司并不一定认真地考虑和讨论过这个问题罢了,它们也就是程序的功能和性能问题,而这两个问题又是其它问题的基础问题,所以它们非常重要,我们需要认真思考和对待。显然,从经验,或者是说从其它任何事情上看,为了我们的目标,我们都不应该只喊口号而不付出真实的行动,或者只是缓慢地行动,那不是我们想要的,至少它不是我所想要的(真对当前状况而言,我可能只会更关心我所做的工作内容),因为它严重地影响着一个公司的发展,影响着我的工作,这是一种思想,我想,从公司的发展看,一个企业的负责团队有责任去思考这个问题。
最早发现的问题是录像的文件名问题。采用扩展名来判别录像文件是由哪种设备产生的,现在看来显然是可笑的,然而,这段代码,在解码库中还是依然存在的,虽然它在当初是发挥了巨大的作用,应该记住它的功劳,但当我们看到它时,也应该记起曾经的狭隘眼光。
和录像文件括展名相关的就是解码信息的存储。所有的文件都没有一个统一的正式的文件头,由此造成的后果是,不允许用户修改录像文件名。这对用户而言,显然是一个过份的要求。所以,我们改了,增加了录像文件头,虽然这个文件头的结构不太好,眼睛睁得不够大,但还是开始行动了,它是一个好的开端,是第一个比较正式的通用的也考虑了版本兼容问题的具有强制性遵守要求的格式文档。它很重要,堪称是一个里程碑。它预示着我们过去陈旧的思想开始发生转变,并且开始付诸行动了。我们可以为之高兴一把,但也应该看到,未来的路还很漫长很艰辛很残酷。实践证明了这个漫长、艰辛、残酷它确实存在,而且决不是夸张!
接着,就是网络协议的版本问题。此问题体现在设备的升级过程中,在深圳公交中,也很明显地体现出来没有定义好协议所带来的一系列问题。当有新功能提出并在实际中发现过去的协议有弊端时,旧的协议已完全不能满足新的要求,所以我必需修改握手协议,还有其中的部分通信协议。这虽然在SDK中做到了前后版本的兼容,但付出的代价却不小,同时,给用户造成的不便,却无法得到修正。在其它的新项目中,也对原来的旧协议进行了稍许修改,因为全部重新定义,将会有大量的工作出现,这主要包括代码的重写、调试、兼容处理三个方面,它会严重影响项目的进度,所以,即使是原来的协议并不“完美”,我们还是得继续使用,不是吗?现在,我们有两种协议:文本协议和二进制协议。虽然当初我认为应该有两套协议和现在的协议数量是一样的,但我所考虑的协议功能和现有的却是不同的,现在的文本协议只是对原来深圳公交协议的简单升级,它只是对原协议的弊端进行了补充,并对协议内容做了适当的修改,但我认为,这和我们公司的现实情况并不相符,我们不仅仅是需要一个更完善的协议,而更需要的是我们的“标准”协议,它应该承载我们未来协议的重担,而不仅仅是一个简单的升级,因为不定义好,我们以后还要改动,还要痛苦地“升级”,而不能简单高效地升级,现在的这种局面,问题已初露端倪,往后的情形,就让我们试目已待吧。
后来,我们又定义了帧头打包格式的标准,此标准可以说是一波三折,直到目前,还疑点重重。当初的定义,由于本人太过武断,未进行广泛地调查,就把结构给定义了下来。结果,结构里面的数据格式却远远不能满足实际的需要,因为各种设备的数据格式不尽相同,为了之间的互相兼容,不得不进行频繁地升级,结果却发现,升级任何一个小的结构体,都会导致组合种类的数量级飙升,小的结构体越多,其飙升的速度就会成倍地增加,这会为以后的兼容处理带来不应有的困难。在这种情况下,不得不把这个标准完全推翻,并重新定义,虽然新的定义和老的在大的结构上一致,但其思想的改变,导致以后的升级会轻松很多,而且升级的频率会大大地降低,然而,这里面还有问题,这问题的成因和过去大家的工作风格是密不可分的,后面会详述。
以上问题,是我所了解稍许的。其问题的形成,除了有历史遗留原因之外,根本上是我们的思想问题,思想没有得到纠正,依然停留在过去那种走一步是一步的原始状态。
在“创业”阶段,这些都不是问题,反而可以提高工作效率,尽快拿出产品,没错,这是起步时的做法。因为我们可以少做考虑,把更多的时间放到产品的推出上,需要有饭吃,才谈得起发展。在往后的时间里,当有了基本保障之后,这种不进行长远考虑的思想,将会把企业禁锢在一个小小的范围之内,其水平只能停留在一定的高度,不可能壮大,甚至会把企业扼杀掉!这时候,在开发新产品的时候,会发现,想做到新产品和旧产品完全不扯上关系那是不可能的事情,比如我们的SDK,不可能为新的产品单独重头写一个SDK,这是显而易见地。问题也随之而来,原有的产品会有缺陷,我们需要修补,如果做旧的产品的时候从来不考虑以后的情况,那么现在的问题很严重,如果原来做了部分考虑,则现在的问题可能会比不考虑的情况少一点点。如果只是升级一次两次,即使麻烦,也不至于做不下去,但如果是要一直升级下去,则终有一天会做不下去的。你会发现,过去的高效率,导致了现在的低效率,也导致了稳定性的日趋下降,日子越久,效率和稳定性都会成倍地降低,由此也导致了后期维护成本的成倍飙升,如果不早做计划,这种情况的发生是不可能避免的。我们现在的情况如何?我认为不容乐观,最起码这上面做得还远远不够。
我们在讨论用户的需求问题,在清楚需求之后,再讨论如何来实现这些功能,再讨论清楚之后,开始付诸行动,一切很顺利。是吗?不!只是现在的情况比早期好了很多,至少上面的步骤基本上都有进行,那么,我就只说现在的问题。现在的问题,有点像黎明前的黑暗,一切都已准备就序,只等太阳升起把大地照亮。这个太阳,就是我们的行动,这里面的东西,就是我们的思想,就是我们要做的产品,这里面的问题,就是太阳还未升起,一切都还未有序。我们思想和产品中的问题,需要我们用行动来解决和消除,这其中首当其冲的就是我们思想的问题解决。我们有两种协议,不说解析不一样,重要的是,里面的常用数据格式不一样,录像文件的常用数据格式和协议里的不一样,协议里的常用数据格式和设备里的不一样,设备里的常用数据格式和设备的外围设备里的又有版本兼容问题,有了文档约定,大家并没有去遵守,设备软件本身的前后版本兼容问题。很久以来,研发部做过联调的同事们应该会有一些感触才对,除了功能的调试之外,有很大一部分的时间花在了这些常用数据的格式上,因为各个地方的格式并不统一,彼此之间为了弄清楚对方的要求,都做了大量的工作,加班到半夜,这个加班,值得吗?这不是造成不稳定的一个因素?这不是效率降低的一种表现?这不是维护成本提高的一种表现?所以,我们需要标准,我们自己的标准,为的是统一,大家都按照这个标准来,谁不符合这个标准,就是谁的责任,就谁来加班,不是更好吗?深圳公交为了升级,文档进行了修改,增加了协议,一切都已就序,设备和PC端的代码都已经完成,本是在下班之前就能完成的,结果联调到半夜12点多才完成,原因是设备的代码和文档不符,结果还要求修改文档,这是一种怎样的思想?我想,做工作说清楚了,大家都会做,只是时间长短不同罢了,认真去做,都做得出来,但思想就不一样了,如果没有雄霸世界的野心,怎会在外国建立军事基地?如果要让美国撤掉外国的所有军事基地,美国的总统肯定不干,因为它的思想就是要雄霸世界,你想让他转变这个思想,太难了。同样,我们过去那随便定义数据格式,或者是有了文档约定却不去遵守的思想要想彻底改掉,去建立大家都“易于”接受的标准,也不是那么容易,但如果没有标准,永远都是处于产品的完善之中,公司不会成熟,不会长大,没有希望,怎么可能留住人才?
4. 标准化的漫长之路
PC端为什么要做SDK?是为了方便其他用户使用我们的产品。其他用户不需要关心我们内部的数据是怎么传递的,也不需要知道内部的数据是怎样存放的,他们只需要关心他们想要的数据,关心这些数据的格式存放。SDK有一个问题,就是版本的升级问题。如果只是功能的增加,也许不成问题,增加接口即可,但如果是复杂的升级,结果就不一样了。比如原来有的数据是一个整数,没有小数位,但由于设备的升级,有了小数位,这时候,接口的数据格式就要改变,用户在拿到新的SDK后,需要修改程序以处理带小数点的数据。更复杂的升级会造成SDK的兼容问题和对用户的使用带来更大的麻烦。比如原有的SDK所处理的某一个参数数据都是3个标识项的,现在有一个新的设备,这个参数却有4个标识项,其中的3个和其它的设备是一样的,那么,SDK如何做到设备之间的兼容?增加一个接口?显示不好,因为都是设置的同一个参数,如果增加了接口,则用户必需真对设备来调用不同的接口,会造成一定的麻烦。这种情况下,SDK会对原来的接口增加一个参数,但这个参数有一个缺省值,由SDK内部确定这个参数有没有用,这样用户在拿到新的SDK后,如果还是在原来的设备上使用,则不必理会这个新增加的参数,只需要重新编译一次工程即可。所以,SDK的功能除了和设备交互之外,很重要的一个功能是对设备的参数进行了加工后传给了上层程序,上层程序不管是对哪一种设备,它们的处理方式都是一样的,即SDK把N种设备都处理成为第N+1种标准的虚拟设备,对用户而言,都是一样的设备。那么,我们定义标准,除了升级之外,也是要让所有的设备都做到统一,尽量做到统一,以便减少SDK的处理繁琐程度,以及增加设备之间的互通性,版本升级的兼容性。实际上,设备也同样应该有一套SDK,这套SDK应该是处理所有外部设备传回的数据,把它们处理成统一的格式,应该具有扩展性,版本兼容性。否则,外围设备发生改变时,设备所收到的数据格式不一样,将会增加工作量。最明显的例子就是GPS原来的2个小数位变为4个小数位所产生的影响,不仅影响到了设备,也影响到了PC的SDK,还影响到了上层程序,影响巨大,你会认为这个改动,花费4个人每人半小时的时间就能搞定吗?如果你这么认为,那太天真了。但如果原来是定义好了,考虑到小数点后10位小数,一开始PC就和设备定义好这样的协议,则这样的变化,PC端就不需要做任何改动,不用花一秒钟的时间,要知道,有那讨论的时间,还不如早点下班休息一会儿也好。但我们确实没有早点下班,反而是为此花费了大量的时间去更正,去讨论,去修改对外的文档,去重新理解这个GPS,还要做到和以前版本的兼容而新定义了一个结构,还要记住新旧GPS之间的差别,当有新人问的时候,还要花费时间去讲述这段历史,以便他能够理解为什么会有两个差不多的结构存在而没有把它们合并成为一个。所以,我们加班,加班来做这些无聊的事情,做这些本来就不应该占用时间的事情。做这些本来就不应该有的工作。今天,你是否将会做很多无聊的工作?是否做了很多无聊的工作?你是否还要加班?
虽然都是程序,但不同的程序是有着很大区别的。这不仅仅是哪种语言的问题,而是一种感性认识的存在,在实际中,这些问题确实存在。如果把我们的SDK限制住,用户只能使用VC来开发,虽然对大部分客户来说都不是问题,但绝对不是所有的客户都没有问题,成都的客户,就是一个活生生的例子。这只是其中的一个表面问题,另外还有一个问题,对于没有使用过c、c++、或者是VC的研发人员来说,如果没有进行过底层的开发,我相信,十之八九都不会关心long型变量占了多少字节,char和TCHAR有什么区别,结构还有字节对齐这样的问题,所以,我们的SDK要做到“通用”,让这一类的开发人员也易于理解,而不是故弄玄虚地再故意弄出几个以位来表示的结构成员,那样只会增加客户的负担,只会增加我们自己的工作量。同样,我们要定义的标准,是为了产品的长远发展,是为了减少工作量,是为了尽快地推出新产品,是为了研发部各小组之间交流的方便。除了也有维护方便性之外,我们还应该看到,我们定义的数据结构,人工来看是次要的,而计算机可以方便地读取和处理是首要的,所以,除了某些特殊的情况之外,我们应该按照计算机的意思来定义数据结构,这样,才会使程序的运行效率提高。基于这些思想,设备就不应该让外围设备的数据格式在设备中到处乱窜,SDK不应该让设备的数据格式在SDK中乱窜,上层的应用程序不应该让SDK的数据格式在SDK的接口上乱窜,能使用数字表示的,就不要使用字符串来表示。只有每一层都严格地把关,最后到了上层的应用程序,才会做到最大限度地统一,上层应用程序才不会因为SDK的改动而大修。设备应该做到数据格式的统一,无法统一的,放到SDK上来统一,如果SDK也无法统一的,只能麻烦上层的应用程序来适应了。同样,设备不应该把难懂的数据发给SDK来解析,SDK应该使用通常语义就能理解的定义来定义数据,这种难以理解的语法和数据,应该在设备内部处理,为什么要把这种难以理解的语义,从头带到尾,让每个环节的研发人员,都难以理解呢?所以我建议,设备端也需要有一套自己的SDK,专门来处理外围设备发来的数据,把它们处理成设备使用的标准数据格式,而且这个SDK对所有的设备都通用,完成一个之后,其它设备都直接使用,岂不美哉?
研发部写软件的同事们,特别是做PC软件的同事们,如果稍有注意,很容易发现,VC的include目录中的文件里,还有msdn的sample文件里和文档的示例中,也包括msdn文档,有很多2000年之前的代码或者文章,1998年的有很多,最早的可能到了1995年,请恕我孤陋寡闻和不好的记性,不知道那里是否有1990年之前的代码和文章,即使现在vc的开发环境已经修改了很多,IDE也大为改观,但1998年的代码还是占到了相当的比例。而听到我们的同事说,不只一个人说过,我们的代码能用上个8年就非常了不起了。8年,是我听到的最长的时间,最短的有说不超过3年的。这大部分是在我定义标准格式的文档时听到的。是啊,我们一开始就没有打算过让我们的代码和产品能稳定地用上50年、100年,计划就是短短几年内就要重写,就要大改,就要把旧的产品踢掉卖全新的产品,有多少人可以做到三年一大改,5年一重写?100个人吗?还是1000个人?而我们现在才几十个人,PC也才不超过10个人,能做到经常大改和重写吗?买我们产品的客户,注定就是要用上3、4年后再买我们的产品?笑话啊,所以,我常为此担心,也因此而无法使定义的标准畅通无阻地执行下去,更可笑的是,先写文档做为标准,再写代码,然后再根据写好的代码来修改标准的文档,难道这还不够荒唐吗?如果是刚毕业的学生,或者是经验不多的研发人员提出这样的问题,我觉得不大奇怪,我也喜欢只用int i, a, b;这样的写法,把a用做超过100行代码的函数中进行时长统计的变量,我也喜欢直接使用int counter这样的全局变量。但是,这应该只是在“新手”的时候才用,在有了“经验”之后,就不应该再有这样的代码出现,最起码不应该让这样的代码留在成形的代码中过夜。我们的研发人员如何?我没有检查过别人的代码,我不敢妄下结论,但有“经验”的都仔细地捉摸一下自己在当前水平的基础上,技术水平是否还有提高的余地。
5. 这就是我想说的
我们现在是有饭吃了,那么,就应该思考一下以后如何才能吃我们想吃的,而不是想着明天可以多赚10块钱而今天要拼命地不加思考地工作,那效率太低了,而且还会越来越低,每天多赚10块钱的计划会越来越受到挑战。
公司的高层,应该多考虑一下如何去制定标准,如何让这些标准能够准确无误地推行下去。这已经严重地影响了现在的效率、稳定和维护,这些问题已经迫在眉睫了,岂能再这样拖延下去?也许一个公司的成长必然要受到这些问题的困扰,但当我们发现的时候,应该及时地去解决,使公司尽快成长起来,而不是面对问题犹豫不决,无所适从,让员工为这些问题而彻夜地加班。在一个体面的公司里上班,特别是在一个高科技的公司里做着体面的工作,走在大街上应该是骄傲和自豪才对,可是我走在大街上如此地匆忙,生活的压力是如此之巨,我未感受到科技带来的轻松,反而被这飞速发展的科技压得气吁吁。
还有,我不想加班。因为加班并不能解决所有的问题,就像有钱并不能解决所有的问题一样简单。
(全文完)