分类目录归档:项目管理

项目管理相关的一些东西,可能是工作中与项目管理相关,或平时看到的一些项目管理的总结

无分支开发

在一个代码库上创建分支的能力是版本控制系统最重要的特性。它的作用是创建一个副本,并将在这个副本上进行操作,从此它就有了它自己的发展方向,也许有一天会回来,回来时就会合并。分支的作用就是为了更好的帮助并行开发,即在同一时刻能够在两个或更多的工作流上面开发,而不会相互影响。分支开会可以用于哪些应用场景呢?

假设我们现在使用的是SVN,并且都是在主干上开发,此时A项目组下在开发,而B项目组准备发布,并且两个项目组中间有一些公共的代码,这个时候就产生了B项目组发布对于A项目组的时间依赖。此时分支开发是一种解决方案。

常见的分支方法有两种:

  1. 在开发时分支。针对不同的项目或开发团队做分支操作,每个项目或开发团队有一个副本,其所有的操作都在副本上进行,在发布时将分支合并到主干,做整体的合并集成,并做整体发布。这是先分后合的招式。
  2. 在发布时分支。主干上开发完成发布后,创建分支,在分支上修改BUG,当修改的BUG发布时将修改的内容合并到主干。这是先合后分再合的招式。

分支是基于并行开发的一种理想状态,即各个分支是完全独立存在的,并且互不干扰,各成系统。但是现实是各个分支基本上都是有一些关联或依赖的。在开发时可能没有感觉,但是当需要将分支的内容合并时,就会产生较多的冲突和问题,虽然现在的版本控制系统已经在这个方面做得相当好了,但是实际操作中还是会存在一些问题,还是会花费较多的时间。因此在做分支开发时,通常会建议以尽可能高的频率将分支的代码合并到主干净,并且确保主干的正常运行,这样会在一定程序上减缓最终实施合并时的冲突。

什么是无分支开发呢?所谓无分支开发就是指所有的开发工作都在主干上进行,当然这是一种理想情况,但是我们可以尽可能向它靠拢。如果实施了无分支开发,此时就不会存在分支、合并、解决合并冲突等问题了,解决合并冲突的问题最好的方法就是永远不要合并,这和赌博不输的最好的办法就是永远不赌一样。为什么说这是理想情况呢?因为在我们的项目中存在大量的依赖,一个文件或一个类,甚至一个函数可能会被多个人或多个团队修改,如果是多个团队的多个项目并行的话,这咱依赖关系将会影响项目的发布。那么怎么减少这些依赖,嗯,是减少,因为我们基本不可能完全去掉这种依赖,这和我们写代码一样,一些有必要的耦合是完全存在的,并且有其存在的必要性。

  1. 迪米特法则。迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),”不要和陌生人说话“,也就是说一个对象应当对其他对象有尽可能少的了解。这是到类级,对象级别的讲究。
  2. 减小需求的粒度。以一种更加敏捷的方式实现需求,将大的需求分割成小的需求,以增量的方式实现并发布。
  3. 使用通过抽象来模拟分支的方式实现代码库中大范围的变更。
  4. 对于公共部分做到文件级的版本发布。即一次发布多个文件的多个版本。
  5. 使用模块和组件,从功能、团队或变更频率等角度对代码进行切割,以解耦代码间的依赖.
  6. 将新功能隐藏起来,直到它完成。

以上的几点更适合于小规模团队。

关于第一点,这是面向对象设计的重要原则之一,除此之外,我们经常还会念叨开闭原则,KISS等等。这些都是实现无分支开发时代码级的一些优化措施。

关于第二点,将大的需求变为小的需求本身就是一个比较困难的工作,拆分的原则是什么?粒度以多大 为优,这些都需要依据实现的情况进行判断,以周为单位?以页面为单位?这种方式会比较轻,可以随时停下,即使需求方向有问题,我们也是可以在较少损失的情况下调头。如果我们坚持这样做,那么就意味着我们在解决一个问题:保持应用的持续可工作。

关于第三点,通过抽象来模拟分支的实现步骤如下:

  1. 当需求分解后还是无法增量开发或增量开发无法解决问题时,考虑引入中间层或抽象层。使要修改的部分与调用者分离,使其直接低耦合。
  2. 在需要修改的那部分系统代码上创建一个抽象层。
  3. 重构系统的其他部分,让他使用这个抽象层。
  4. 创建一种新的实现代码,在它完成之前不要将它作为产品代码的一部分。
  5. 更新抽象层,让他使用这个新的实现代码。(如果没有完善的回滚机制,建议在此处做切换开关,即以一个变量控制抽象层的实现,当然,这并不是一个优雅的解决方案)。
  6. 移除原来的实现代码
  7. 如果不再需要抽象层,就移除它。

关于第四点,对于公共部分的代码,其修改频率较之其它功能模块更频繁,并且可能存在需要发布旧版本的需求。

关于第五点,模块化,组件化在架构设计中现在已经是非常普遍的行为,但是要想完全模块化或组件化,其难度较大,尽可能的向这方面靠拢,以一定的规则划分模块,针对模块进行设计实现,并在最后将所有模块的结果有机的结合起来。这是在设计层面的分支和合并操作的替代品。

关于第六点,将新功能隐藏起来,直到它完成。这是在没有增量式发布,小步快跑的前提下,或需求确实无法拆分,一组特性一定要一起实现,此时通常会开一个分支做这些新功能的发布,而无分支开发中,可以将这部分代码放在主干,但这些功能对用户不可见,以某种配置项(文件或数据库都行)的方式来操作其对用户的可见性。

《持续交付》总结一

项目延期和重构

项目背景及延期原因分析

这是一个用作宣传的系统,它由客户端,Flash端,服务端三部分组成, 客户端放在客户机器上,实现一些本地的播放及相关操作; Flash端被嵌入到客户端中,调用服务端的数据。 服务端做播放内容的管理及提供数据接口服务。

现在已经有一个1.2的版本在生产环境运行。现在的需求是增加一些功能并且将旧的耦合较多的部分进行重构,以插件的方式提供播放内容。

这三块分别是三个研发部门抽调的开发人员实现,并且开发人员没有换到一个区域办公,人在不同的楼层。 考虑到这是一个不到9人月的小项目,所以在项目估算的时候并没有做基于代码行的估算,而是以一种比较粗犷的工作量估算方法。 在估算过程中,有针对这三块功能进行分别估算,但是在Flash端的估算过程中没有体现出这是一个以细化需求为目的的估算过程。 开发在正常的进行,项目一切正常,然而由于其它项目需要,Flash端的开发人员需要进入其它项目, 于是将此项目的开发工作交接给另一个开发人员,此时Flash端的开发进度就开始脱离了项目经理的掌控。 等到服务端和客户端的开发完成,测试完成,风险时间用完,Flash端的开发工作才基本完成。但是Flash端是客户端和服务端的中转地, 起着至关重要的作用。待开发工作完成后,进行需求确认,发现现有的功能较旧版有较多出入,一部分必须有的功能并没有实现, 实现的功能中还有较多的BUG。于是项目延期……

原因分析

基于项目管理的角度分析整个项目过程,存在以下问题:

  1. 估算过程问题
  2. 人员变更问题
  3. 项目经理进度把控问题
  4. 风险识别不充分

估算过程应该是一个细化需求,指导开发人员更了解所开发功能的过程,从而在对需求的理解上对整个开发周期进行估算。 此项目估算过程过于草率,没有体现估算的价值。 人员变更没有在项目管理过程中体现,在人员变更时对于工作的交换及需求学习等活动都不充分,甚至没有。 虽然有一些客观原因,但是项目经理确实没有实时的现场的跟进Flash端开发的进度,过于充分相信开发人员对于进度的描述。 风险识别过程不充分,特别是人员变更及采用工作量估算时,没有对风险有一个充分的识别。

重构对项目的影响和如果可以重来

基于代码开发的角度,主要是Flash端重构的问题。 重新认识下Martin Fowler在《重构》这本书中对于重构的定义:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。

我们这次重构是有必要的,在需求列表中有将旧的耦合较多的部分进行重构,以插件的方式提供播放内容的需求项。 但是从现状来看,这次的重构已经不再是纯粹的重构了,它变成了一个重写的过程。旧的所有的功能都重写了,并且一些资源的使用都是采用的新的。 没有认清重构的本质,没有定义好重构的范围,这是此次问题的关键所在。 不改变代码外在行为的前提在本次重构中没有体现,并且对于外在行为的定义和识别活动也没有进行,这是一个问题。

如果可以重来,重构前开发人员对于原有代码外在行为的进行需求识别和功能细节识别, 这个可以简单点,开发人员给出一个check list,产品、测试都可以使用这个list, 同样这也是自己重构过程中的指向灯。在给出列表后,产品和测试人员需要对这个列表进行审核, 确认是否和已经的外在行为一致,这些活动在开发前和开发完成后都要进行。

另一些思考

  1. 基于不同的技术的项目,可能项目经理对这些领域不了解,但是可以看到实际的产出,在一些功能实现完成后,尽量到开发人员的位置上确认其描述。
  2. 对于跨部门的项目,虽然推动其它部门的人做事有一定的难度,但是事是必须要做的,需要更多的关注和协调。
  3. 如果可以,项目组成员集中在一个区域是最好的,不过有时候也只是想想而已。