这是几天前在知乎上看到的问题,切入点还蛮有趣的,试着答一下吧。
- 开发效率与执行效率,我们应该怎样斟酌?题目链接
简单分析
老规矩,先简单分析一下题目:
- 开发效率通常反映在两点上:1.开发新功能是否迅速;2.修复缺陷是否及时。
- 所谓运行效率,通俗地说就是(生产环境下)系统运行速度快,平均响应时间短,均匀流畅。不管是功能上还是用户体验上,都没有不必要的卡顿和等待。当因为某个因素性能开始恶化时,恶化的情况能随该因素的增强而有一定的收敛。
既然谈到取舍问题,显然题主已经预设了“二者之间至少存在一定程度上的负相关”这一隐含假定。那么这二者是否的确是此消彼长的对立关系呢?俺觉得还是值得讨论的。下面俺将分别从 a) 项目类型和开发模式 b) 产品质量和工程质量 c) 项目管理和团队建设 这三个角度具体地谈一下。
项目类型和开发模式
在项目中前期,通常可以清晰地通过以下几点来了解项目的基调和开发模式:
- 项目的大致开发周期和预期的团队规模
- 行业内同类型项目的横向对比
- 技术骨干的经验和背景
有同学会问,(作为团队一员),这些因素虽然不难了解到,可是跟咱们讨论的开发效率和运行效率有什么关系?俺要说,这个关系可不小。项目开发中的很多变量,从立项的第一天起就被初始化成常量了。俗话说“兵熊熊一个,将熊熊一窝”,一个团队的战斗力高低和能量释放程度,先天的基因是一个很重要的因素(一不小心掉到基因决定论里了),下面我们具体看一下。
对于强调快速迭代,小步前进的敏捷团队而言,通常项目规模也不会很大,复杂度也会较行业内同类型项目为低,如果能适当地辅以一些局部的深入挖掘和特色创新,就可以形成一定的差异度和竞争力了。
这种项目通常不具备压倒性的优势,也很可能(在初期)缺乏足够的市场推广资源,这种时候,相对较高的开发效率和迅速灵活的反应能力就成了生死存亡的关键。在这种情况下,对于技术骨干而言,最重要的素质在于审慎地控制复杂度,绝大部分情况下不要主动选择花样作死,避免在无谓的细节上纠缠不清,尽量采用业界惯常做法(必要时可适当简化)。 通过设计和流程上的管控 (而非细枝末节的优化),让运行时的性能始终保持在可控的安全区域以内,是省心省力且比较有效的手段。回过头来比较开发效率和运行效率,孰轻孰重,孰易孰难,就不难权衡了。
对于大型组织内的大型项目,就是另一番情景了。
先说结论,大型系统内的开发效率和运行效率相干性并不高,通常都与参与者的“整体上对系统的熟悉程度”和“问题域的相关经验”严重相关。
为什么这么说呢?跟每个开发人员都得独当一面的小团队不同,大型组织内的成熟系统,往往有着相对较细致的团队分工和相对完善的开发流程。整体而言,大型组织的开发效率总是相对偏低是很正常的,正如越宽阔的江河流速越缓慢是一个道理。
再来看运行效率,由于庞大的系统有着巨大的惯性,大大小小的框架在经年累月的开发积累中已然成型,内部隐含着往往是海量的“不足为外人道”的专门知识和方案(以及大量的 workarounds 和 hacks),在这种情况下,哪怕是理论上非常优越(架构级和算法级)的优化,实践中都有可能被系统中的各种细节牵跘,以致无法达到预期的效果,有时甚至还会出现导致性能下降的咄咄怪事。
总得来说,对于大型系统而言,开发效率和运行效率相关性并不显著。对于主导者而言,由于比小团队拥有多得多的资源可供调配,这两者不再是孤立的因素,而是需要从整体上去协调,规划和取舍的诸多因素之二。对于参与者而言,“整体上对系统的熟悉程度”比“有更多的学院派的理论和技巧”在日常开发中更具价值;“问题域的相关经验”比“写代码手速高/能刷ACM”在招聘求职时更有参考意义。
产品质量和工程质量
在工作中,我们经常会听到有人这么说:“xxx做功能很快的,三天一个模块,五天一个系统” 云云。其实我从小就很羡慕这样的同学,他们头脑清晰,思维敏捷,见机果断,手脚麻利,再复杂的问题丢给他们,也能须臾间找到要害,让你不得不拍腿感叹:“硬是要得!”。而我自己则是一个反应有点迟钝木讷,经常慢半拍的人。同样是一件事,我经常要回过头慢慢想想,才能弄得明白来龙去脉。
后来随着年岁渐长,我慢慢懂得,快有快的优势,慢却也有慢的好处。在日常开发中,一味图快,唯快不破,速度至上,往往会导致工程质量的低下。更有一类开发团队,打了鸡血一样生冲硬突,其实只是在弥补和掩饰指挥者内心的不安,慌张,焦虑和信心不足。
摘录两段纯银老师的话以增兴味吧:
我们都知道,产品并不是功能越多越强就越有竞争力。你拼命加班,飞快迭代,发布各式各样的型号版本,把自己搞得鸡血沸腾,但最终决定胜负的并不是速度,而是精度。拿我很钦佩的Instagram举例子,至今不开发Android版也不去完善网页端,平均一两个月才更新一个版本,不到一年用户数已经突破了700万大关!故而产品的理念与方向,比速度与激情更重要得多——但我看国内很多团队就知道抄,东抄西抄,自己的想法很少,就算有想法也往往是“抄这家”“抄那家”的贪多求全。这样的6X12,6X14又有何意义呢?真没见过几款产品单单靠“抄得快”“抄得全”就能成气候。勤不仅不能补拙,还有可能造成设计过度与产品失衡,结果越补越拙……
– 出自 唯快不破?
这个行业对速度的无条件信仰,已经到了病态的地步。不计其数的例子证明,市场表现最好的那个创新者,并不是同类中的第一个,甚至未必是前十个,也未必是版本更新最勤快的那个,而是产品最具感染力的那个。但大家还是在头上绑着「唯快不破」的布条向前冲锋。「快」的确是个好事,却不是万试万灵的真理。快速发布和产品感染力之间没有必然联系,如同多试错和找准路之间也没有必然联系。在创新市场强调速度,本质上是用体力支出来弥补信心不足。但体力并不是唯一的竞争项目。
– 出自 杂念·6月(下)
当然了,纯银老师更多的是在谈产品,这里我们为免偏题,只谈技术。
俺认为,良好的设计决定了系统的基因。在设计良好的系统中,干正确的事情比干错误的事情更容易,更快;在恰当的场合提供方便的脚手架,让开发和测试更便利;良好的解耦和内聚,让开发者在局部改动中可以“从心所欲而不逾矩”。充斥不良设计的系统中,开发人员会觉得处处掣肘;再小的修改也会时刻担心影响到完全不相干的东西;不仅新功能难以快速实现和验证,优化同样难以实施,或勉强实施后实际效果与预期相差甚远。两者之间,开发效率和运行效率可谓天差地远。
其次,为团队提供所能提供的最好的工具。工具的作用不应被低估。更好的硬件,更短的编译时间,甚至更舒适的椅子,都会让开发效率和工程质量提高不少,这方面的研究和结论非常多,我也就不再赘述了。软件开发是一项以人为核心的活动,“在开发人员身上节省项目成本”是我能想象到的最铺张浪费的管理行为。
除此之外,俺还在实践中体会到一个心得:**深思熟虑后一气呵成的模块,反而会有相对较高的质量,不太会出问题;那些一直以来没有想通想透,虽然看起来功能运作良好,但仍需反复修改调整的系统,在将来只会需要更多的修修补补。**这一点王垠同学在 “半年来的工作感受” 和 “谈’测试驱动开发’” 这两篇文章中 (王垠同学的 blog 最近无法访问,感兴趣的同学可以自行搜索),也有类似的有趣表达。我选择了一些摘录如下:
……(前略) 在 Google 的六个月里,我无视同事对于测试的要求,从无到有的做出了如此精密的系统,一个测试都没有写照样做得好,为什么呢?因为我的代码非常的简单清晰,我随时都可以把它们完整的呈现在头脑里面,从而让“心灵之眼”可以看到可能出现的错误。也许这就是所谓的“逻辑思维”。
对测试过分依赖的人,往往不具有这样的思维能力。他们不能够看到代码最简单的本质,所以需要做很多试探,以求达到“近似解”。为了不至于偏差很多,就写很多测试,用以捕捉和防止每一次的错误。这就像一个初学画画的人,一点一点的描,用橡皮反复的擦,可总也抓不住事物的精髓。
……(中略)……
当我的代码需要大量的测试才能确保正确的时候,那就是它该被推翻重写的时候。所以我的代码往往没有任何补丁和变通,可以说是无懈可击。这就像是一个真正会画画的人,他闭目沉思,然后一气呵成。当然,优美的代码并不是一蹴而就的,有的代码被我推翻重来几十次才最后成功,但我最后的代码不留下丝毫错误的痕迹。所以我觉得,看一个程序员的水平,不要看他留下来多少行代码,而要看他删掉了多少行。
……
-- 出自 “半年来的工作感受” (王垠)
是的,真正的优美源于简单和优雅(Simplicity & Elegance),这是我与上面摘录文字的最大共鸣,也是为什么软件开发除了作为工程活动的一面之外,还展现出类似于园艺活动那样的艺术特质。总得来说,俺认为,真正的开发效率来源于对问题域的深刻认识,这绝非一朝一夕之功,也绝非某个"绝顶"算法的花拳绣腿,或某种“技巧性”的实现所能涵盖。我们真正欣赏和追求的高效开发,应该是庖丁解牛式的深厚技艺中举重若轻水到渠成的“快”,而非赶鸭子上架的加班赶工中匆匆写就的勉强可以运行的代码。
美的程序不可能从修修补补中来。它必须完美的符合事物的本质,否则就会出现上面的情况,有许许多多无法修补的特例。程序员跟画家其实差不多……(中略)……在修改代码的时候,你必须用“心灵之眼”看见代码背后的事物。这也是为什么很多高明的程序员不怎么用调试器(debugger)的原因。他们只是用眼睛看着代码,然后闭上眼,脑海里浮现出其中信息的流动,所以他们经常一动手就能改到正确的地方。
-- 出自 “谈'测试驱动开发'” (王垠)
所以写出简单正确的代码,远比写出运行效率高的代码要重要得多。在保证正确性的前提下,一个系统越简单,当遇到瓶颈时改善它的性能就越容易。而且程序员工作时的心情和效率,与代码质量关系很大。对比曾经历过的项目,我发现:在结构良好,流畅整洁的代码上工作时,往往心情愉快,能持续高效地产出所谓“优美的”代码(逻辑漏洞少,性能较好);在混乱(甚至“肮脏”)的代码上工作,往往很难鼓起勇气去修改,强忍着草草了事,代码质量也偏低,bug不断。
在工程质量角度,最后再补充一句略有“负能量”的话吧,如果不顾团队的具体情况,不能切实可靠地制定方案,一味贪多务得,疲于追求开发效率,那么再好的 codebase 都会很快地腐烂。而且这个过程通常是不可逆的——想把好代码弄糟很容易,把糟代码弄好可就难了。当代码糟到一定程度时,优秀的开发人员就会不可阻挡地离去,那么离游戏结束也就不远了。
项目管理和团队建设
现在我们假设开发团队有 A, B 和 C 三种人,其中 A 是经验非常丰富的领域专家,B 是正处于当打之年的程序员,C 是经验不足但有成长欲望的新人。那么如何搭配才算是比较理想的比例呢?
虽然不同的项目情况相差非常大,但我在实践中慢慢体会到,一个相对比较理想的比例大致是 1:2:3。一个 A 掌好舵,保证你的方向,两个 B 是你的左膀右臂,保证你的速度和力量,三个 C 是你的生力军,保证你的潜力和活力。保持一个相对平衡的团队结构,从长远上总是比堆人的效率高得多。另外再说一下,这是一个非常非常主观和粗略的估计,这么简单粗暴地给个数字,对于具体的项目情境通常是不靠谱的,还需要负责任的指挥者去量体裁衣,以免削足适履。
想必大家也已经看出来了,决定这一系统的关键就是 A 的质量。而往往由于 A 又要肩负一定的沟通(主要指对外部团队和需求),行政(对组织而言)和管理(对内部团队)的责任,无法做到全身心地投入在开发上,发挥的作用往往要打一个折扣。这一点,即使是公认战力超高的卡神亦难免俗,卡神在 Rage 发布前夕 (2011年8月,ZeniMax 收购了 id software 两年后) 说了如下一番话:
If anything, since the ZeniMax acquisition, it's been great. I don't even have to pretend to be an executive anymore. I don't have to go to board meetings. I don't have to do anything! I can just sit in my office and work.
My core is defined as being an engineer. I take resources and a goal, and I try and put them to the best use to get us there. That's what I do. I don't want to be doing anything else.
如果说 ZeniMax 的收购改变了什么的话,到目前为止都挺好的。我再也不必假装是个管事儿的了。不需要再参加董事会议,也不必“不得不”做什么事儿了,我终于可以踏实地坐在办公室里干活了。
本质上我是一个工程师。给定一些资源和一个目标,我会尽可能地找到最理想的运作方式来实现这个目标,这就是我想做的,除此之外的其他事情我毫无兴趣。
我想,除了少部分比较幸运的同学,对大部分技术专家而言,在现在以及未来很长的一段时间内,不用考虑商业上的妥协,能完全按照理想的步调和节奏去完成一部心目中的佳作,仍只是一个遥远的梦想。这大约就是所谓的“人在江湖,身不由己”罢。
写在最后
杂七杂八地说了这么多,到最后已经有点歪楼了。那么放几句应景的话,与大家共勉吧。
“当你被迫把东西做得很简单时,你就被迫直接面对真正的问题。当你不能用表面的装饰交差时,你就不得不做好真正的本质部分。”
「當你擁有的資源越少、擁有越少的時間去思考,這些限制才能夠逼你作出真正關鍵的決定。而不是讓你在思慮周詳之後作出一個廢物。」
「尝试将有用的人从杂音中分离出来,将有意愿帮忙的人从一大堆无聊评论中分离出来是很难的。在Linux 8086项目中我的确错误地放弃了这一目标,如何将那些只会空谈而又无所事事的人弄走是一门学问。」
“只有在成为某个领域的专家之后,你才会听到心里有一个细微的声音说:“这样解决太糟糕了!一定有更好的选择。”不要忽视这种声音,要培育它们。优秀作品的秘诀就是:非常严格的品味,再加上实现这种品味的能力。”
“The physics of software is not algorithms, data structures, languages and abstractions. These are just tools we make, use, throw away. The real physics of software is the physics of people.”
(全文完)