转鲍志云同学的一篇好文章(关于实际项目开发中的代码复用)

77 views
Skip to first unread message

pongba

unread,
Dec 11, 2007, 11:57:42 PM12/11/07
to TopLanguage

来源:http://wesleybao.spaces.live.com/blog/cns!B8C72620C46CF4CA!2855.entry


写太多腐败话题了,要平衡一下。今天我们继续讨论技术话题,本期内容——亘古不变的话题:复用。

为了提高engineering organization的效率,复用(或者说don't reinvent the wheel,不要重新发明轮子)是很重要的。

关于复用的故事,通常是这样开始的...

先有了一个产品,然后做第2个产品时可能从第1个产品那里借鉴了一部分设计和代码;做第3个产品时又从前两个产品那里借鉴了一些设计和代 码。一开始,可能代码是copy-paste过来的,然后随着不同产品各自的版本延伸,这些原本一致的代码慢慢变得细微不同。然后原来做这些产品的工程师 逐渐发现了这一点,开始觉得这样不好了,于是某位大老板一挥手,说我们来consolidate一下吧。

所谓consolidate么,大致就是搞一下code merge,精简一下重复的内容。于是大家从这些成熟产品中归纳出一些公共组件,单独标版本号,成立一个team单独维护,其他产品都复用这些组件。

一开始,一切都很美好,不再有重复但又有细微不同的代码要维护。后来逐渐发现,产品A用的X组件的1.2版本和Y组件的1.5版本,产品B 用的X组件的1.8版本和Y组件的2.0版本... 那么这些不同的版本都要维护,这可不又成了重复但又有细微不同的代码了么,于是维护组件的team不干了,说大家统一用最新的吧。于是产品team说用最 新的可以但你要负责保持向下兼任性。

可是,向下兼任谈何容易啊(在Intel实现x86 CPU指令集的同学们和在微软实现Win32 API的同学们应该深有体会),这意味着你一旦在早先的接口设计中犯下一个错误,你以后每年都要为这个错误付出持续的代价。那么,大家都是人不是神,错误 几乎是不可避免的,知道有更好的做法而不去做也是很折磨人的,对追求完美的工程师而言尤其如此。

于是结果就是,组件team努力去向下兼容,但还是会有一些问题,产品team还是不得不每次有新版本组件就升级。这有点像逆流而上, 你要花很大的努力才能保持自己站在原地不后退,做一个产品的新版本,常常仅仅是因为组件升级了,为了让产品依然能用不得不和新版本的组件集成。在这个逆流 而上的游戏中,engineering organization花了很多代价,但使用产品的客户却并没有获得太多好处。

这样的复用,是自下而上的,效果却并不是很好。于是,有人觉得,或许我们应该自顶向下地规划哪些东西可以复用。

这种做法有个好听的名字,叫product line engineering。具体内容是,既然我们知道都有哪些产品线,要做哪些产品,我们就找个负责整个产品线的构架师,来分析一下这些产品之间的异同吧。 (所谓commonality analysis和variability analysis)。不同的部分,自然是单独做。相同的部分,自然是抽出来做个公用的component。

不过呢,这种做法却有点过于理想化。如果产品和组件分别要升级怎么办?如果有新的商机,要推出原本产品线规划之外的新产品,或者并购了其他公司的其他产品线应该怎么办?这个... 基本上很难办。

那么,看来自顶向下的做法也会遇到很多问题。于是,很多人开始退而求其次,甚至有些比较愤青同学开始觉得,复用是对的,但要追求代码复用却是不现实的。

事实上这样说也不是全无道理。当你复用了太多代码,也就有了太多的外部依赖性和集成开销。这在PM看来都是风险。我们要尽可能做自包含的产品,减少外部依赖,这样才能降低风险,让项目更可控。

有同学表示反对,说这与我们的常识不符啊,我们从小就知道复用代码是好的,copy-paste是evil的,你怎么让我们别复用代码了呢。

于是,有同学站出来做分析。大家安静...安静... 想一想,你们的上一个产品,花了多长时间做出来?现在如果让你们原样重做一遍,大概要花多少时间? 嗯... 大概上次一半的时间就行了吧。那么为什么会省一半的时间呢?因为上次有些东西我们不知道怎样做,需要摸索,现在知道了。这就对了... 做一个产品,花得时间最多的部分,不是写代码,而是学习,去弄明白客户的需求,去弄明白怎样用现有的技术去实现这些需求,或者去弄明白怎么发展出一些新技 术去实现这些需求。那么,如果这些学习的effort可以复用,我们不就已经省下好多时间了吗?我们捡了西瓜就行了,不要太贪心非得连芝麻一起捡...

确实,可以复用的东西有很多,我们可以让在一个产品中做过某种事情的人去另一个项目做类似的事情,这是复用了他的知识/经验;类似的 feature的设计文档也可以彼此借鉴,这是复用了设计。真正写代码的effort在整个项目中占的比重其实不多,我们不一定那么强调代码的复用的。而 且,对于engineer而言,学习怎么使用一个proprietary的component的interface,和实现这个component本身 feature相比,未必花的时间就少很多,但乐趣却会少很多。或许这也部分解释了为什么这么多人喜欢重新发明轮子吧。毕竟发明的乐趣是无穷的,而发明全 新的东西却是很难的不是所有人都做得到,发明一个更好的现有的东西相对容易一些。

在engineering organization方面,也可以做一些变动,有利于促进想法的交流和effort的复用。比如,可以让engineer除了隶属于某个 project team,还属于某个(virtual的)feature team。这样的话,不同project的类似feature其实都是由同一个feature team完成的,可以有很多种办法来促成的探索/学习的collaboration和design的collaboration。一个最简单的做法是为每 个virtual feature team建一个mailing list,另外每个mailing list都找一位相应领域比较有研究的engineer来coordinate和扮演consultant的角色(所谓domain expert或者subject matter expert),这位engineer其实有点virtual feature team lead的意思,那么创建出这些职位也有利于鼓励各位engineer钻研技术、走technical track。这些virtual feature team是动态的松散的组织,某个engineer在研究和实现某个feature时属于这个feature team,转去做另一个feature时就属于另一个feature team了。当然如果他愿意做新feature的同时还留在原来的feature team把走过的路积累的经验传承给新加入这个feature team的engineer,这也是好事,但不鼓励同一个engineer同时属于太多的feature team,除非这位engineer当时没有project assignment,专门负责帮助别人。另外,随着产品线的延伸,会不断有新的feature team组建。这些feature team在某种程度上有些类似study group或者workshop,只不过主题更直接地和工作相关。这样的engineering organization是学习型组织,在做产品的同时,大家也在共同探索新的领域、协同学习。

当然,把共同的功能抽出来做成组件还是要鼓励的,虽然这种做法不完美。就好比治疗癌症,没有某种方法可以一定奏效,但很多种不完美的做法一起使用(也就是所谓鸡尾酒疗法),那么总体效果还是不错的。

关于组件的多个版本并存的问题,我们可以给组件的每个版本设一个end of life的时间,给产品team一些时间,也适当减少一些组件team的维护开销。关于interface是否向下兼任也可以以类似的做法,旧的 interface可以标为legacy但还会支持一段时间。

对于已成熟的产品线,我们可以用自顶向下的product line engineering的方式来做,系统化地进行代码复用。对于新的产品和并购来的产品线,要容许起初时间的一些重复,但可以慢慢整合进现有产品线。事实 上成熟的公司到一定时候多半都会有一个或多个自己的技术平台/框架,有较好的弹性和较强的技术吸纳能力。

鼓励做正在做不同产品的类似功能的engineer多多交流,促进其他形式的复用,也是很必要的。要设计利于交流的 engineering organization。读过《第5项修炼》或者玩过beer game(供应链游戏)的同学应该知道,organization structure是会影响人们思考问题和做事情的方式的。

==== 幕后故事 ====

怎么会没来由地写这个话题呢?因为认识的某位SDE同学(Senior Director of Engineering) 比较喜欢"来事",每过段时间就要找人出主意看看现状有没有什么地方可以改善。(当然,在engineering organization里要耍得开,会"来事"是很重要的。)那么,当企业做大之后,原来的高管忙不过来了,就把organization切成一小片一 小片顺便提拔一批人来管,这个本身没有错。但是呢,Wesley同学觉得,不管把一个organization怎么切(按照customer profile来segmentation也好,按照line of business来切也好),至少engineering organization是不应该随着一起切的,切开了之后各管各的,会有很多duplicated的effort,这不是没事找事增加成本吗。但是呢, Wesley同学又不好建议说偶们再把切下来的蛋糕拼回去然后把重复部分扔掉把多出来的人裁掉... 那么,就不痛不痒地建议同学们关注一下复用吧...

要让profit增长,有两种办法,开源和节流。Wesley同学比较喜欢开源,不喜欢节流。但是呢,如果非要节流,非要降低cost 的话,那么至少在engineering cost方面,促进复用、减少浪费的duplicated effort是一个很有效的做法,至少要比减少娱乐开支或者控制差旅费用等等有效得多。

katkat lim

unread,
Dec 12, 2007, 12:05:01 AM12/12/07
to pon...@googlegroups.com
这种复制来复制去的事,我们就在做着。而且以后还得做。。。
项目数目少了还好办,要是多了起来,就完全不知道哪个是哪个了。

red...@gmail.com

unread,
Dec 12, 2007, 12:53:00 AM12/12/07
to pon...@googlegroups.com
好文, 学习.

pongba 写道:

buhongbo

unread,
Dec 12, 2007, 8:24:55 PM12/12/07
to pon...@googlegroups.com
我有点异想天开:能不能借鉴UNIX的系统设计思想呢?
有很多细小的工具,他们能很漂亮地完成自己的一个非常特别的任务。
然后用脚本把这些工具组织起来完成一个复杂的任务。
 
有可能把一个任务也做成一种类似操作系统核心加外围工具软件这种方式么?
 

buhongbo
2007-12-13

发件人: pongba
发送时间: 2007-12-12 12:57:51
收件人: TopLanguage
抄送:
主题: [TopLanguage] 转鲍志云同学的一篇好文章(关于实际项目开发中的代码复用)
 

来源:http://wesleybao.spaces.live.com/blog/cns!B8C72620C46CF4CA!2855.entry


写太多腐败话题了,要平衡一下。今天我们继续讨论技术话题,本期内容——亘古不变的话题:复用。

为了提高engineering organization的效率,复用(或者说don't reinvent the wheel,不要重新发明轮子)是很重要的。

关于复用的故事,通常是这样开始的...

先有了一个产品,然后做第2个产品时可能从第1个产品那里借鉴了一部分设计和代码;做第3个产品时又从前两个产品那里借鉴了一些设计和代码。一开始,可能代码是copy-paste过来的,然后随着不同产品各自的版本延伸,这些原本一致的代码慢慢变得细微不同。然后原来做这些产品的工程师逐渐发现了这一点,开始觉得这样不好了,于是某位大老板一挥手,说我们来consolidate一下吧。

所谓consolidate么,大致就是搞一下code merge,精简一下重复的内容。于是大家从这些成熟产品中归纳出一些公共组件,单独标版本号,成立一个team单独维护,其他产品都复用这些组件。

一开始,一切都很美好,不再有重复但又有细微不同的代码要维护。后来逐渐发现,产品A用的X组件的1.2版本和Y组件的1.5版本,产品B 用的X组件的1.8版本和Y组件的2.0版本... 那么这些不同的版本都要维护,这可不又成了重复但又有细微不同的代码了么,于是维护组件的team不干了,说大家统一用最新的吧。于是产品team说用最新的可以但你要负责保持向下兼任性。

可是,向下兼任谈何容易啊(在Intel实现x86 CPU指令集的同学们和在微软实现Win32 API的同学们应该深有体会),这意味着你一旦在早先的接口设计中犯下一个错误,你以后每年都要为这个错误付出持续的代价。那么,大家都是人不是神,错误几乎是不可避免的,知道有更好的做法而不去做也是很折磨人的,对追求完美的工程师而言尤其如此。

于是结果就是,组件team努力去向下兼容,但还是会有一些问题,产品team还是不得不每次有新版本组件就升级。这有点像逆流而上,你要花很大的努力才能保持自己站在原地不后退,做一个产品的新版本,常常仅仅是因为组件升级了,为了让产品依然能用不得不和新版本的组件集成。在这个逆流而上的游戏中,engineering organization花了很多代价,但使用产品的客户却并没有获得太多好处。

这样的复用,是自下而上的,效果却并不是很好。于是,有人觉得,或许我们应该自顶向下地规划哪些东西可以复用。

这种做法有个好听的名字,叫product line engineering。具体内容是,既然我们知道都有哪些产品线,要做哪些产品,我们就找个负责整个产品线的构架师,来分析一下这些产品之间的异同吧。(所谓commonality analysis和variability analysis)。不同的部分,自然是单独做。相同的部分,自然是抽出来做个公用的component。

不过呢,这种做法却有点过于理想化。如果产品和组件分别要升级怎么办?如果有新的商机,要推出原本产品线规划之外的新产品,或者并购了其他公司的其他产品线应该怎么办?这个... 基本上很难办。

那么,看来自顶向下的做法也会遇到很多问题。于是,很多人开始退而求其次,甚至有些比较愤青同学开始觉得,复用是对的,但要追求代码复用却是不现实的。

事实上这样说也不是全无道理。当你复用了太多代码,也就有了太多的外部依赖性和集成开销。这在PM看来都是风险。我们要尽可能做自包含的产品,减少外部依赖,这样才能降低风险,让项目更可控。

有同学表示反对,说这与我们的常识不符啊,我们从小就知道复用代码是好的,copy-paste是evil的,你怎么让我们别复用代码了呢。

于是,有同学站出来做分析。大家安静...安静... 想一想,你们的上一个产品,花了多长时间做出来?现在如果让你们原样重做一遍,大概要花多少时间? 嗯... 大概上次一半的时间就行了吧。那么为什么会省一半的时间呢?因为上次有些东西我们不知道怎样做,需要摸索,现在知道了。这就对了... 做一个产品,花得时间最多的部分,不是写代码,而是学习,去弄明白客户的需求,去弄明白怎样用现有的技术去实现这些需求,或者去弄明白怎么发展出一些新技术去实现这些需求。那么,如果这些学习的effort可以复用,我们不就已经省下好多时间了吗?我们捡了西瓜就行了,不要太贪心非得连芝麻一起捡...

确实,可以复用的东西有很多,我们可以让在一个产品中做过某种事情的人去另一个项目做类似的事情,这是复用了他的知识/经验;类似的 feature的设计文档也可以彼此借鉴,这是复用了设计。真正写代码的effort在整个项目中占的比重其实不多,我们不一定那么强调代码的复用的。而且,对于engineer而言,学习怎么使用一个proprietary的component的interface,和实现这个component本身 feature相比,未必花的时间就少很多,但乐趣却会少很多。或许这也部分解释了为什么这么多人喜欢重新发明轮子吧。毕竟发明的乐趣是无穷的,而发明全新的东西却是很难的不是所有人都做得到,发明一个更好的现有的东西相对容易一些。

在engineering organization方面,也可以做一些变动,有利于促进想法的交流和effort的复用。比如,可以让engineer除了隶属于某个 project team,还属于某个(virtual的)feature team。这样的话,不同project的类似feature其实都是由同一个feature team完成的,可以有很多种办法来促成的探索/学习的collaboration和design的collaboration。一个最简单的做法是为每个virtual feature team建一个mailing list,另外每个mailing list都找一位相应领域比较有研究的engineer来coordinate和扮演consultant的角色(所谓domain expert或者subject matter expert),这位engineer其实有点virtual feature team lead的意思,那么创建出这些职位也有利于鼓励各位engineer钻研技术、走technical track。这些virtual feature team是动态的松散的组织,某个engineer在研究和实现某个feature时属于这个feature team,转去做另一个feature时就属于另一个feature team了。当然如果他愿意做新feature的同时还留在原来的feature team把走过的路积累的经验传承给新加入这个feature team的engineer,这也是好事,但不鼓励同一个engineer同时属于太多的feature team,除非这位engineer当时没有project assignment,专门负责帮助别人。另外,随着产品线的延伸,会不断有新的feature team组建。这些feature team在某种程度上有些类似study group或者workshop,只不过主题更直接地和工作相关。这样的engineering organization是学习型组织,在做产品的同时,大家也在共同探索新的领域、协同学习。

当然,把共同的功能抽出来做成组件还是要鼓励的,虽然这种做法不完美。就好比治疗癌症,没有某种方法可以一定奏效,但很多种不完美的做法一起使用(也就是所谓鸡尾酒疗法),那么总体效果还是不错的。

关于组件的多个版本并存的问题,我们可以给组件的每个版本设一个end of life的时间,给产品team一些时间,也适当减少一些组件team的维护开销。关于interface是否向下兼任也可以以类似的做法,旧的 interface可以标为legacy但还会支持一段时间。

对于已成熟的产品线,我们可以用自顶向下的product line engineering的方式来做,系统化地进行代码复用。对于新的产品和并购来的产品线,要容许起初时间的一些重复,但可以慢慢整合进现有产品线。事实上成熟的公司到一定时候多半都会有一个或多个自己的技术平台/框架,有较好的弹性和较强的技术吸纳能力。

鼓励做正在做不同产品的类似功能的engineer多多交流,促进其他形式的复用,也是很必要的。要设计利于交流的 engineering organization。读过《第5项修炼》或者玩过beer game(供应链游戏)的同学应该知道,organization structure是会影响人们思考问题和做事情的方式的。

==== 幕后故事 ====

怎么会没来由地写这个话题呢?因为认识的某位SDE同学(Senior Director of Engineering) 比较喜欢"来事",每过段时间就要找人出主意看看现状有没有什么地方可以改善。(当然,在engineering organization里要耍得开,会"来事"是很重要的。)那么,当企业做大之后,原来的高管忙不过来了,就把organization切成一小片一小片顺便提拔一批人来管,这个本身没有错。但是呢,Wesley同学觉得,不管把一个organization怎么切(按照customer profile来segmentation也好,按照line of business来切也好),至少engineering organization是不应该随着一起切的,切开了之后各管各的,会有很多duplicated的effort,这不是没事找事增加成本吗。但是呢, Wesley同学又不好建议说偶们再把切下来的蛋糕拼回去然后把重复部分扔掉把多出来的人裁掉... 那么,就不痛不痒地建议同学们关注一下复用吧...

徐梓鼎

unread,
Dec 12, 2007, 9:25:25 PM12/12/07
to pon...@googlegroups.com
我现在的做法是,C++写基础的代码,其他的业务逻辑用python来做,效果MS不错

在2007-12-13来自buho...@gmail.com的邮件[[TopLanguage] 转鲍志云同学的一
篇好文章(关于实际项目开发中的代码复用)]中写到:



> 我有点异想天开:能不能借鉴UNIX的系统设计思想呢?
> 有很多细小的工具,他们能很漂亮地完成自己的一个非常特别的任务。
> 然后用脚本把这些工具组织起来完成一个复杂的任务。
>
> 有可能把一个任务也做成一种类似操作系统核心加外围工具软件这种方式么?



--
DEBUG是件快乐的事情 <xuzi...@gmail.com>


Kyle M. Lee

unread,
Dec 13, 2007, 7:14:06 PM12/13/07
to pon...@googlegroups.com
pongba 写道:
> 先有了一个产品,然后做第2个产品时可能从第1个产品那里借鉴了一部分设计和
> 代码;做第3个产品时又从前两个产品那里借鉴了一些设计和代码。一开始,可
> 能代码是copy-paste过来的,然后随着不同产品各自的版本延伸,这些原本一致
> 的代码慢慢变得细微不同。然后原来做这些产品的工程师逐渐发现了这一点,开
> 始觉得这样不好了,于是某位大老板一挥手,说我们来consolidate一下吧。
>
> 所谓consolidate么,大致就是搞一下code merge,精简一下重复的内容。于是
> 大家从这些成熟产品中归纳出一些公共组件,单独标版本号,成立一个team单独
> 维护,其他产品都复用这些组件。
>
以前偶曾经就呆在过“组件化”小组。

> 可是,向下兼任谈何容易啊(在Intel实现x86 CPU指令集的同学们和在微软实现
> Win32 API的同学们应该深有体会),这意味着你一旦在早先的接口设计中犯下
> 一个错误,你以后每年都要为这个错误付出持续的代价。那么,大家都是人不是
> 神,错误几乎是不可避免的,知道有更好的做法而不去做也是很折磨人的,对追
> 求完美的工程师而言尤其如此。
>
其中的痛苦只有其中的开发者才知道,而且有时候明明在新版本中修正了某些接口
的bug,或者提升了性能,为了兼容,还要接受一个参数以恢复旧有的接口机制。
结果产品做很大,自己很不爽。而且在组件化项目中,需要Leader非常强势,才可
以推动接口的进化。而往往维护阶段,各个产品线的兄弟就过来要求改这改那,以
满足各个特化的要求,然后,分裂,仲裁,。。。 一堆麻烦事情。

> 当企业做大之后,原来的高管忙不过来了,就把organization切成一小片一小片
> 顺便提拔一批人来管,这个本身没有错。但是呢,Wesley同学觉得,不管把一个
> organization怎么切(按照customer profile来segmentation也好,按照line
> of business来切也好),至少engineering organization是不应该随着一起切
> 的,切开了之后各管各的,会有很多duplicated的effort,这不是没事找事增加
> 成本吗。
>
把组织切一切,就是矩阵管理,呵呵~ N多人做重复的事情,同时发现每个人的职
务变多了。哈哈~

真是一篇好文~ 好文采,好内容。
signature.asc

pi1ot

unread,
Dec 13, 2007, 8:32:17 PM12/13/07
to TopLanguage
所以说,接口设计很重要,我们又回到了起点。
> signature.asc
> 1K下载
Reply all
Reply to author
Forward
0 new messages