中小型项目中的敏捷开发 AGILE IN MODERATE-SIZED PROJECTS
0 前言
敏捷开发,已经是一个老生常谈的话题了,同时也引发了很多讨论。这里不是为了论证哪种敏捷开发模型更优,每种项目管理模型都是适合其产生的大环境及技术趋势。本文要讨论的话题是,在如今强调 敏捷开发、DevOps 的大趋势下,为什么要花时间去思考合理的项目管理模型选型,以及中小规模的项目的开发人员,应该怎么做。
1 是什么
1.1 敏捷开发
敏捷开发 的定义这里不再赘述,其核心思想在于尽可能多的将资源集中在软件开发本身,同时,依赖与用户的密切沟通和交流,提供不断更新的解决方案,实现快速交付和持续交付。 敏捷开发通过迭代和增量开发的模式,相比传统的瀑布开发,可以更早的验证产品的可行性。更重要的是,用户本身也是开发的参与者,用户的反馈、意见和需求影响者产品的发展,同时也降低了产品成本和风险,因此也逐渐成为目前软件开发领域最受关注的项目管理模型之一。
1.2 误区
- 工作的软件高于详尽的文档
并非不需要文档,只是不需要将大部分精力放在文档的形式之上。关键功能的需求设计文档是项目非常重要的资源。
- 个体和互动高于流程和工具
并非不需要完善的开发流程。规范的开发流程能够极大程度减少软件产生缺陷的概率,例如良好的Devops实践能够缩短每个开发周期,提高效率。
- 响应变化高于遵循计划
并不代表没有规划。尽管在实际开发中,会出现某些需求的优先级被临时提高(尤其是针对小型项目),但是始终会围绕中长期规划进行迭代,这样的好处是能够防止产生一些"简单粗暴"的实现,来避免可预见范围内的重复工作。
1.3 小瀑布陷阱
敏捷开发强调在一个迭代周期内完成整个 瀑布模型 的全部生命周期(需求->设计->开发->测试->维护),而在单个迭代内,由于初期规划不合理或需求不明确、发生变更等情况,导致某个阶段的时间占比过长,而其他角色出现等待的情况,这种情况就叫做小瀑布陷阱。在较大型的项目中,可能由多位产品策划负责不同的多个业务模块,而大多数情况下,负责开发的往往是同一批技术人员,因此一旦发等待,将导致开发人员会在不同的上下文中进行切换(类比操作系统,这也是造成资源浪费的情况之一)。
2 为什么
2.1 角色与角色的矛盾
“The hardest single part of building a software system is deciding precisely what to build.” – Frederick P. Brooks, Jr.
我们知道,在同一个产品的开发周期中,会有多个角色承担不同的职能共同协作。 由于不同角色的认知和利益点的差异,导致每个角色的关注点不同。产品策划倾向于关注需求的投入(如人力、实体资源等)产出(如用户、营收增长等)比。开发人员考虑需求对整体系统的影响、实现成本以及寻求更优的架构设计和解决方案。设计师则期望带给用户最好的视觉体验和最优的交互逻辑,而最关键的,用户更关注是否解决了自己的问题。
在这种场景下,“对齐"不同角色的关注点并(由于资源有限)进行取舍,则是 Brooks 口中的 what to build。
2.2 自研(Build From Scratch)与微调(Finetuning)的矛盾
一定程度上我们可以下一个结论,绝大多数场景下的产品需求,都绝非从未被实现过的需求,它们只是侧重点不同,或是不同需求的组合。因此,借用机器学习领域的概念,自研还是在现有方案(甚至是最佳实践)的基础上进行微调也是矛盾之一。
自研带来的好处是显而易见的,每个环节都没有历史包袱,开发人员也没有技术债务,不需要考虑前向兼容和数据迁移同步等工作,相当于投入一个全新的项目之中(同时可以顺便引入一些很 cool 的特性或技术)。但是代价是需要投入更多的成本去满足从零开始的一切需求。
而微调,在可预见的范围内是最佳实践。避免重复造轮子、开源协同也是基于 站在巨人肩膀上 的考虑。通过引入通用的代码库、组件、协议、规范、算法模型、微服务等作为 局部最优解 ,快速实现需求并推动业务上线是预期内最大程度上的"敏捷”。不过微调的问题也很明显,造好的轮子不一定适合现有业务的需求;当需要依赖第三方服务/组件并可能有一些定制化需求时,合作方也不一定会及时响应,在这种场景下,接入的成本可能会高于从零开发;并且当依赖的第三方服务过多时,整体系统的稳定性相当于分散到了每个下游服务中。
根据上面的分析,如何选择实际上取决于需求本身的性质。当关联的需求是核心竞争力时,即使短期内可以依赖第三方,但最终还是难以避免彻底自研。除此以外,则要综合考虑投入和产出的关系,来选择自研还是微调。
2.3 产品研发流程与敏捷的矛盾
我们期望通过敏捷研发丢弃掉冗余的开发流程,让每一个小迭代推动产品升级,但当多个需求同时进行时,可能会出现不同需求抢占资源(人力、发布排期)的情况,甚至代码评审和(必要的)需求评审也会由于参与人员的重叠而产生冲突。因为代码缺陷是难以预期和避免的,修复这些缺陷、解决用户提出的问题往往并不在规划的排期之内,而只要涉及代码更新,都将占用一个完整的迭代流程。
2.4 业务与技术的矛盾
回到开发人员本身,很难解决的一个问题是,怎样平衡 业务 需求和 技术 需求。 用户只能感知到 业务 的部分。例如,产品增加了开屏广告、静态资源加载时间变短、某个按钮移动了位置或改变大小等等,这些是用户能够直接感受到的内容。而类似接入了广告系统、优化代码性能或者更新了前端组件,作为实现这些业务需求的技术准备,用户并无感知,并且开发者也不希望用户感知到,尽管这些需求是实打实的消耗了开发人员的时间和精力。 业务需求的增加同时也伴随着原有的架构与目前用户体量的不匹配;性能出现瓶颈,甚至是需要还技术债;这些情况出现的时候,开发人员只能和产品经理或者项目负责人沟通,从而争取到解决这些问题的时间。
3 怎么做
3.1 要了解全貌
我认为,作为开发人员,是最应该了解产品/软件全貌的人。从时间维度上看产品需求,包含了
-
已经发布的特性
它们已经成为了产品的一部分,开发人员需要熟悉或了解这些特性,一方面当产生问题时,可以快速响应和解决,另一方面在未来的开发中,可能会涉及到历史特性的升级、重构或变更,是否熟悉这些特性决定了后续的开发效率。 -
开发中的需求
无需多言,当开发人员对手上的需求足够了解,才能避免产生问题。 -
规划中以及可能出现的需求
尽管在日常开发中,规划中的需求大多数情况下不会第一时间被开发人员感知,但实际上我认为开发人员需要了解这些内容。不单是有助于以一种更远的视角看待目前的工作,另一方面,开发人员对这些规划的反馈能够让需求"PK"在事前,使资源更容易整合。
阅读项目的相关文档、多参与代码评审,以及阅读其他人的代码有助于开发人员更好的了解整个产品。
3.2 规范和规范化
产品研发中最困难的一环就是对齐不同角色的期望,而沟通则是解决这个问题的关键。因此 规范 和 规范化 至关重要。规范不仅能够让大家更好的协作,还可以在基本问题上减少不必要的沟通。并且,例如设计规范、研发规范、专业名词词典等可以量化或实体化的内容也能更好的使一部分工作流程自动化。
在需求开发之前,和项目组成员同步设计思路和方案,提供相关的说明文档;需求开发之中,为核心代码添加足够的注视和说明,以及测试用例;而开发完成后,进行代码评审。规范化的过程也能够让团队每个成员参与并更加了解项目,当一个项目下所有人遵循同一个规范时,巴别塔就成了。
3.3 一架天平
MVP: 最小可行产品
“One of the most important lean startup techniques is called the minimum viable product. Its power is matched only by the amount of confusion that it causes, because it’s actually quite hard to do. It certainly took me many years to make sense of it.” by Eric Ries
MVP 似乎成为了目前产品开发的最佳实践 —— 快速推出一个新的 feature 的简单实现,根据用户反馈或一些指标数据不断进行优化和重构,最后成为一个完整的功能。 不过要警惕,在 MVP 版本中,可能包含着很多的不稳定因素,如不安全的实现、不完善的边界条件处理、潜在的不兼容性(尤其包括其他稳定发布的特性对此MVP版本的临时兼容)等,这些很有可能会在未来成为技术债务。
预先设计: 做好抽象
“‘Abstraction, Abstraction, Abstraction’ are the three most important things in programming.” by Paul Hudak
有位同事分享过一个观点,软件开发的本质是对现实世界的建模。顺着这个角度,抽象则决定了建模的复杂度,以及是否能把"现实世界"的本质转换为一行行代码。同时,好的抽象能够很大程度上提高代码的可复用性、扩展性和可维护性(虽然前期需要投入更多精力进行好的设计)。 面向对象编程的 SOLID 原则有助于指导我们更好的进行抽象。
过度设计:第二系统效应
“Premature optimization is the root of all evil.” by Donald Knuth
但预先设计的过程,很容易走向另一个泥潭,第二系统效应,即为了尽可能地使系统具备完善的功能,在前期做了许多不必要的优化和设计,导致迭代延期;而新的需求接踵而至,为了追平进度则进行快速修补和大量的 hardcode ,进而转变为技术债务… 所以开发者需要根据经验和对项目的了解,在可控范围内进行预先设计并规划好重构,这将极大程度上提高开发效率。
总结一下就是,从 MVP 到 过度设计,就像是一架天平的两端,需要谨慎的处理使其达到平衡。做好抽象(前),提高代码质量(中),及时重构(后),则是维持平衡的关键。
3.4 尝试冲刺 - 极限编程(XP)
极限编程 具体的内容在这篇引文中有比较详细的介绍。对于中小型项目(或者大型项目中的某个阶段性需求)来说是个值得一试的实践。尤其针对一些从新开始的需求,极限编程更适合一些没有历史包袱的需求,可以快速进行设计、开发和验证。由于较少的人参与,可以更加快速的进行沟通和讨论,在开发过程中强调效率和质量,因此没有历史包袱的功能更容易展开。
4 总结
对于开发人员来说,为了实现研发效能最大化,每个项目成员都应该 遵循项目组的研发规范,同时建立良好的沟通机制,通过规范化的文档和代码评审流程尽可能提高每个成员对项目的了解程度。在需求开发之前 进行良好的设计和规划,这样在开发过程中避免产生一些技术债务,同时谨慎的评估自研和引入第三方组件成本。对于一些新的想法可以尝试通过 MVP 进行快速验证,并尝试跟小组成员一起进行冲刺;而对于日常需求和长期规划,采用完整的研发流程能够保证整个项目的健康发展。
5 其他参考
-
No Silver Bullet: Essence and Accidents of Software Engineering https://www.cgl.ucsf.edu/Outreach/pc204/NoSilverBullet.html
-
Minimum Viable Product: a guide http://www.startuplessonslearned.com/2009/08/minimum-viable-product-guide.html