极客时间《软件工程之美 - 宝玉》及 《知行合一:实现价值驱动的敏捷和精益开发》学习总结。
软件项目的开发其实是一个工程,整个开发过程是可以有效组织起来的;对于开发过程的各个阶段,已经有很多解决问题的最佳实践及方法来帮助我们高效完成任务;还可以借助工具来协助管理,提升开发效率。
软件工程是一门用工程化方法解决软件项目问题的学科。无论什么岗位,什么职责,只要你从事软件开发相关的工作,都绕不开软件工程,因为不管是工作方法论、团队协同亦或者流程,万变不离其宗,核心都是软件工程。软件工程就是围绕软件开发过程,产生的方法学和工具,聚焦于质量,构建和维护高质量的软件。-- 宝玉
1970和1980年代的软件危机。在那个时代,许多软件最后都得到了一个悲惨的结局,软件项目开发时间大大超出了规划的时间表。一些项目导致了财产的流失,甚至某些软件导致了人员伤亡。
鉴于软件开发时所遭遇困境,北大西洋公约组织(NATO)在1968年举办了首次软件工程学术会议,并于会中提出 “软件工程” 来界定软件开发所需相关知识,并建议“软件开发应该是类似工程的活动”。
演化历史:从瀑布模型到敏捷开发
瀑布模型的诞生,在当时是有非常重大的意义的,让软件开发从无序到有序,让大家更好的分工协作,同时每个阶段又衍生出各自的方法学和工具,例如需求分析、软件测试等等。然而瀑布的特性决定了它只能从上往下流,而且从上到下走完整个周期很长,所以一旦出现了需求的变更,将会非常痛苦,很多事情需要重头再来。
基于瀑布模型,又衍生出 V 模型、原型设计、增量模型、螺旋模型 等模型,试图改善瀑布模型存在的一些缺陷。
到了 90 年代,各种轻量级开发方法例如 Scrum、极限编程等也不断被提出。 2001 年,这些轻量级开发方法一起组成了敏捷联盟,其后敏捷开发如同星星之火,逐渐形成燎原之势。
近些年,云计算、微服务这些新技术的产生,也对软件工程产生了影响。让分工更细,很多企业可以将运维、服务器维护、DBA、甚至某些独立服务交给云服务商;微服务让大团队变成小团队,每个小团队可以更专注于细分领域,减少相互之间的依赖。
《软件工程——实践者的研究方法》作者 Roger S.Pressman 画了一张图,高度概括了整个软件工程的核心知识。
软件工程 = 工具 + 方法 + 过程
关于过程,要构建高质量软件,则要解决软件过程中的混乱,将软件开发过程中的沟通、计划、建模、构建和部署等活动有效地组织起来。
关于方法,方法是指在整个过程中,如何构建系统的方法学。比如,如何分析用户需求;如何对产品进行测试;如何进行系统设计等。
关于工具,用来来辅助方法的执行,提高效率。把一些手动的工作自动化,比如自动化测试工具,自动构建部署工具;通过工具,可以帮助把一些流程规范起来,比如 Bug 跟踪、源代码管理;还可以通过工具,帮助提高编码效率,比如各种编辑器 IDE。
ACM与IEEE Computer Society修定的SWEBOK Software Engineering Body of Knowledge)提到,软件工程领域中的核心知识包括:
- 软件需求(Software requirements)
- 软件设计(Software design)
- 软件建构(Software construction)
- 软件测试(Software test)
- 软件维护与更新(Software maintenance)
- 软件配置管理(Software Configuration Management, SCM)
- 软件工程管理(Software Engineering Management)
- 软件开发过程(Software Development Process)
- 软件工程工具与方法(Software Engineering Tools and methods)
- 软件质量(Software Quality)
项目管理的定义:指在项目活动中运用专门的知识、技能、工具和方法,使项目能够在有限资源限定条件下,实现或超过设定的需求和期望的过程。项目管理是对一些与成功地达成一系列目标相关的活动(譬如任务)的整体监测和管控。这包括策划、进度计划和维护组成项目的活动的进展。
五大过程组
十大知识领域
软件工程的目标是:在给定成本、进度的前提下,开发出具有适用性、有效性、可修改性、可靠性、可理解性、可维护性、可重用性、可移植性、可追踪性、可互操作性和满足用户需求的软件产品。追求这些目标有助于提高软件产品的质量和开发效率,减少维护的困难。
(1)适用性:软件在不同的系统约束条件下,使用户需求得到满足的难易程度。
(2)有效性:软件系统能最有效的利用计算机的时间和空间资源。各种软件无不把系统的时/空开销作为衡量软件质量的一项重要技术指标。很多场合,在追求时间有效性和空间有效性时会发生矛盾,这时不得不牺牲时间有效性换取空间有效性或牺牲空间有效性换取时间有效性。时/空折衷是经常采用的技巧。
(3)可修改性:允许对系统进行修改而不增加原系统的复杂性。它支持软件的调试和维护,是一个难以达到的目标。
(4)可靠性:能防止因概念、设计和结构等方面的不完善造成的软件系统失效,具有挽回因操作不当造成软件系统失效的能力。
(5)可理解性:系统具有清晰的结构,能直接反映问题的需求。可理解性有助于控制系统软件复杂性,并支持软件的维护、移植或重用。
(6)可维护性:软件交付使用后,能够对它进行修改,以改正潜伏的错误,改进性能和其它属性,使软件产品适应环境的变化等。软件维护费用在软件开发费用中占有很大的比重。可维护性是软件工程中一项十分重要的目标。
(7)可重用性:把概念或功能相对独立的一个或一组相关模块定义为一个软部件。可组装在系统的任何位置,降低工作量。
(8)可移植性:软件从一个计算机系统或环境搬到另一个计算机系统或环境的难易程度。
(9)可追踪性:根据软件需求对软件设计、程序进行正向追踪,或根据软件设计、程序对软件需求的逆向追踪的能力。
(10)可互操作性:多个软件元素相互通信并协同完成任务的能力。
软件项目的开发其实是一个工程,整个开发过程是可以有效组织起来的;对于开发过程的各个阶段,已经有很多解决问题的最佳实践,有很多方法来帮助我们高效完成任务;我们还可以借助工具来协助管理,提升开发效率。基于软件过程,我们有了角色分工,有了对过程的管理和工具,对过程中每个阶段细分的方法学和工具。
时间(多久可以完成)、范围(需要实现多少功能)、成本(花多少钱)这三个要素直接决定了产品的质量(产品的质量,客户的满意度)
软件危机:
1.软件开发的成本和进度难以准确估计,延迟交付甚至取消项目的现象屡见不鲜
2.软件存在着错误多,性能低,不可靠,不安全等质量问题
3.软件成本在计算机系统的整个成本中所占比例越来越大
4.软件维护极其困难,而且很难适应不断变化的用户需求和使用环境。
当软件的规模越来越大,复杂度不断增加,软件项目开发维护过程中的问题就逐步暴露出来:软件产品质量低劣、软件维护工作量大、成本不断上升、进度不可控、程序人员无限度地增加。所以在 60 年代,“软件危机”的概念被提出来。
软件工程是软件行业知识体系的内核。无论你想走技术路线,还是转向做管理,想要走的更快更稳,那就绕不开软件工程。
(1)用分阶段的生存周期计划进行严格的管理。
(周全计划–项目概要计划、里程碑计划、项目控制计划、产品控制计划、验证计划、运行维护计划)
(2)坚持进行阶段评审。(尽早发现问题)
(3)实行严格的产品控制。(变更控制、保持一致性)
(4)采用现代程序设计技术。(提高效率、方法大于力气)
(5)软件工程结果应能清楚地审查。(审查标准、易于管理)
(6)开发小组的人员应该少而精。(影响质量效率、管理)
(7)承认不断改进软件工程实践的必要性。(CMMI、过程改进、实践经验总结)
美国著名的软件工程专家巴利·玻姆(Barry Boehm)综合这些专家的意见,并总结了美国天合公司(TRW)多年的开发软件的经验,于1983年提出了软件工程的七条基本原理。
玻姆认为,这七条原理是确保软件产品质量和开发效率的原理的最小集合。它们是相互独立的,是缺一不可的最小集合:
在制定目标时,遵循SMART原则,即:
Specific(明确的):目标应该按照明确的结果和成效表述。
Measurable(可衡量的):目标的完成情况应该可以衡量和验证。
Aligned(结盟的):目标应该与公司的商业策略保持一致。
Realistic(现实的):目标虽然应具挑战性,但更应该能在给定的条件和环境下实现。
Time-Bound(有时限的):目标应该包括一个实现的具体时间。
掌握工程思维,把每件事都当作一个工程项目来推进。把一件事情分成几个阶段:分析、设计、实施、测试、完成,然后制定相应的计划。这种方法不仅非常有效,让我的做事效率大幅提高,能够更全面地、站在更高的角度去思考。
什么是工程方法?有目的、有计划、有步骤地解决问题的方法就是工程方法。工程方法通常会分成六个阶段:想法、概念、计划、设计、开发和发布。如果你用这六个或者其中几个阶段对照日常工作和生活中遇到的问题,会发现绝大部分问题都可以看成一个项目,并且拆分成几个阶段,按照计划一步步完成。
想法:想法阶段通常是想要解决问题。最开始问题通常是模糊的,所以需要清晰地定义好问题,研究其可行性,检查是否有可行的解决方案。
概念:概念阶段就是用图纸、草图、模型等方式,提出一些概念性的解决方案。这些方案可能有多个,最终会确定一个解决方案。
计划:计划阶段是关于如何实施的计划,通常会包含人员、任务、任务持续时间、任务的依赖关系,以及完成项目所需要的预算。
设计:设计阶段就是要针对产品需求,将解决方案进一步细化,设计整体架构和划分功能模块,作为分工合作和开发实施的一个依据和参考。
开发:开发阶段就是根据设计方案,将解决方案构建实施。开发阶段通常是一个迭代的过程,这个阶段通常会有构建、测试、调试和重新设计的迭代。
发布:将最终结果包括文档发布。
用工程方法去处理事情,有两点好处:
1. 有一个被有效论证过的方法论指导你,可以帮助你提高成功概率,也可以提高效率。
2. 当你用工程方法去思考的时候,你会更多的站在整体而非局部去思考,更有大局观。
分工带来的好处,就是复杂的任务可以分给不同的人来做,这也有助于技能的专业化,提高工作效率。如果只站在自己的立场去考虑问题,没有人关注整体价值,就容易相互误解,产生矛盾、增加成本。
以下这些工作场景,估计你不会陌生。
这样的场景问题还有很多,这在很大程度上都归因于大家只是站在自己岗位的角度来看问题,没有站在项目的整体角度来看。
如果能站在项目整体来看问题,你就会去关注项目的质量、项目的进度、项目的成本、项目的最终用户,那么上面这些场景将变成:
工程思维,本质上是一种思考问题的方式,在解决日常遇到的问题时,尝试从一个项目的角度去看待问题、尝试用工程方法去解决问题、站在一个整体而不是局部的角度去看问题。
快速原型模型:用户自己也对需求不明确。
增量模型:需求明确、可模块化,按模块采用瀑布。
迭代模型:需求多变,按时间分阶段、持续交付,控制范围、重构。
敏捷开发:非模型,价值观+原则+指南+最佳实践。客户参与+沟通,方法选择决策符合敏捷原则。可运行的软件>详尽的文档、客户合作>合同谈判、响应变化>遵循计划。
极限编程的“极限”(Extreme),意思就是如果某个实践好,就将其做到极限。比如:如果做测试好,就让每个开发人员都做测试 ;如果集成测试重要,就每天都做几次测试和集成 ;如果简单的就是好,那么我们就尽可能的选择简单的方法实现系统功能。
Scrum
瀑布模型的典型问题就是周期长、发布烦、变更难,敏捷开发就是快速迭代、持续集成、拥抱变化。敏捷开发就是想解决瀑布模型这样的重型软件开发方法存在的问题,用一种轻量的、敏捷的方法来改善甚至是替代它。
敏捷不是一种方法论,也不是一种软件开发的具体方法,更不是一个框架或过程,而是一套价值观和原则。
敏捷开发中流行的站立会议,主要目的是为了保证团队成员充分的沟通,遇到困难可以及时寻求帮助。但是如果每天的站立会议流于形式,并不能起到有效的目的,则应该减少频度,甚至取消换成其他方式。要不要在你的项目开发中使用站立会议,判断的依据就在于这样做是不是符合敏捷的价值观和原则。也就是说,当你开发做决策的时候,遵守了敏捷开发的价值观和原则,不管你是不是用 Scrum 或者极限编程,那么都可以算是敏捷开发。
敏捷开发模型和瀑布模型的差异。
敏捷开发的 Sprint 和迭代模型的迭代有什么区别?
在上一章我介绍了增量模型和迭代模型,这两种也是一种快速迭代的方式,那么敏捷开发和迭代模型的区别是什么呢?
我们假设有两个团队,都要实现一个简单的用户系统,一个团队用迭代模型,一个团队用敏捷开发(Scrum),一个迭代 /Sprint 的时间周期都是 2 周(10 个工作日)。
迭代模型所在的团队,产品经理会先花 2 天时间去分析需求,写成需求分析文档,架构师会花 3 天时间来做设计,程序员会花 3 天时间编码,测试再花 2 天时间去测试,最后上线用户系统。
再看敏捷开发的团队,Product Owner(类似于产品经理)会把需求拆分成了几个简单的用户故事:用户登录、用户注册、找回密码、修改资料,然后放到当前 Sprint 的 Backlog(任务清单),Team(开发团队)成员开始从 Backlog 选择用户故事。
程序员 A 选了“用户登录”这个用户故事,他会去找 Product Owner 确认需求细节,之后动手实现这个用户故事。
功能完成后,同时程序员 A 还写了单元测试代码和集成测试代码,对登录的功能写了自动化测试。完成后,通过持续集成工具测试和部署到测试环境。部署完成后,用户登录功能就可以进行使用了。
这个过程,程序员 A 可能花了 4 天时间,做完“用户登录”这个用户故事之后,他又开始继续选取“找回密码”的用户故事来做,4 天时间也完成了。
其他程序员也和程序员 A 一样,他们也会从 Backlog 选择一些用户故事来做。
当团队中第 1 个用户故事部署完之后,测试人员就开始帮助测试,发现的 Bug 都提交到了 Backlog,程序员们在完成用户故事后,开始着手修复这些 Bug,正好在最后 2 天都修复完成。
从上面的例子,你可以看出,迭代模型本质上是一个小瀑布模型,所以在一个迭代里面,需要完整的经历从需求分析,到设计、编码、测试这几个完整的阶段。
所以像瀑布模型一样,刚开始测试的时候是不稳定的,到测试后期才逐步稳定下来,一般迭代前期也会相对轻松一点,而后期测试阶段可能会时间很紧张。
敏捷开发的 Sprint 中,没有像瀑布模型那样严格的开发阶段划分,而是一个个循环迭代的 Sprint。举例来说,一个瀑布模型的项目,可能会按照阶段分成:2 周需求分析,2 周设计,4 周编码,2 周测试,然后上线发布,一共 10 周。如果用敏捷开发的方式来进行,那么可能会是每 2 周一个 Sprint,每个 Sprint 结束后,都会发布上线,每次发布的可能只是完整功能的一部分,但是每次发布的都是一个可用的版本,通过多个 Sprint 的迭代,最终完成项目开发。
具体到每一个 Sprint 的开发周期中,在一个 Sprint 中会有多个小的开发任务,这些开发任务主要是新功能的开发和 Bug 的修复。由于每个 Sprint 周期很短,所以不能像瀑布模型那样有充足的时间去做需求分析、设计和测试,那么敏捷开发中怎么保证质量呢?
在敏捷开发中,通常用“用户故事”这样的方式来代替传统的需求分析,也就是以用户故事的形式,对一个需求进行简单的描述,配合关键的测试用例,并且和需求方的紧密沟通,让开发人员可以理清楚需求;通过“只做刚刚好的设计”来节约设计上的时间;通过“自动化测试”、“持续集成”来提升测试效率。
相对来说,敏捷开发中,整个 Sprint 的节奏是比较恒定,产品也是相对稳定的,即使用户故事没有完成,也不影响版本的发布。
因此,敏捷开发更注重软件开发中人的作用,需要团队成员以及客户之间的紧密协作。
需要满足一些条件,例如:
因为敏捷开发对项目成员综合素质要求更高,做计划要相对难一些。如果团队大、客户不配合、领导不支持,再好的敏捷方法也很难有效实践起来。
用瀑布模型盖房子:
客户想要盖一栋房子(初步的想法)
客户一开始可能没想清楚想要什么样子的房子。(客户对需求还不清楚)
施工方开始找客户确认:用途是什么,要个几层的房子,什么建筑风格,希望什么时间完工,预算多少。(问题定义)
施工方根据客户提的需求,对比工期和预算,评估是不是值得做。(可行性研究)
施工方评估后觉得可行,于是和客户签订合同,约定价钱和工期。(立项,制定项目计划)
施工方开始跟客户沟通确认需求,例如每层户型如何,将来的装修风格等。(需求分析)
确认完需求后,施工方开始出建筑施工图,还画了漂亮的建筑效果图。(系统设计和 UI 设计)
施工方按照设计图开始施工。(程序编码)
这期间如果客户去参观施工情况,客户只能看到毛胚,只有最后施工完成才能看到最终样子。(在中间客户看不到结果,只有最后能看到结果)
原定二层是两个卧室,在房子施工过程中,突然客户说两个卧室不够,要改成三个卧室。这意味着施工方要对施工图重新设计,很多已经建好的房间要拆掉重建。(瀑布模型是很难响应需求变更的,而且越到后期代价越大)
工程质量检查人员对施工结果进行质量检测,如果不满足质量要求,需要修改。(测试)
最后验收通过后,客户入住。(上线)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2021031418051497.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xpZmVSaXZlcg==,size_16,color_FFFFFF,t_70#pic_center =400x
如果用敏捷的方式盖房子:
客户想要盖一栋房子(初步的想法)。
产品经理和客户进行了初步的沟通,把用户的需求写成了一个个用户故事(用简单的用户故事代替繁重的需求文档),例如:
作为一个上班族,我想要一个卧室,以便于休息;
作为一个家庭主妇,我想要一个厨房,以便于做饭。
施工人员根据用户故事和客户进一步沟通(客户合作高于合同谈判),然后对用户故事进行设计和实现;
每个用户故事开发时,还要给一个测试机器人编写测试脚本,让机器人可以自动测试(大量采用自动化测试),并且做好的用户故事可以随时被测试验收(随时发布,持续集成);
每个 Sprint 四个星期时间(时间盒子,迭代时间固定);
第一个 Sprint 搭了个草棚,一张床就是卧室,厕所就挖了一个坑,厨房还来不及搭建(每个 Sprint 会选择高优先级的用户故事),屋顶还在漏水(每个 Sprint 会定期发布,客户可以随时看到可用版本,即使还不完整);
第二个 Sprint 有了简易厨房,同时修复了屋顶漏水的毛病(每个 Sprint 不仅完成用户故事,还会修复 Bug);
第三个 Sprint 升级成了小木屋,但是忘记加上窗户(敏捷推崇自动化测试,但可能会测试不完备);
第四个 Sprint 升级成了砖瓦房,窗户也开好了,客户可以入住。但是这时候客户发现一家三口的话,完全不够用,需要扩建到 3 个卧室。于是决定下个迭代改成 3 个卧室(响应变化高于遵循计划);
第五个 Sprint,升级成了 3 个卧室,升级过程中把厨房下水道弄坏了(迭代过程中可能会导致质量不稳定);
第六个 Sprint,修复了下水道的问题,房子也装修好了(迭代中不断完善);
客户验收使用(上线)。
用敏捷开发的方式,不再像瀑布模型那样有严格的阶段划分,会在迭代中不断完善;不再写很多文档,而是和客户一起紧密合作;不再抵制需求变更,而是即时响应变更;不再等到测试阶段才发布,而是随时发布,客户随时可以看到东西。
当然,采用敏捷开发的模式也存在一些问题,例如全程需要客户参与,由于测试相对少一些 ,问题也会相应多一些。
一切工作任务围绕 Ticket 开展早些年的项目开发,都是围绕着项目计划开展的,把甘特图打印贴在墙上,方便团队成员看项目进展到什么地步了。自从敏捷化后,开始变成了看板。
所谓的看板,就是把白板分成几个栏,每一栏为一类,分别写着“To Do(待选取)”、“In Progress(进行中)”、“Done(完成)”等,再把工作任务变成一个个五颜六色的即时贴,根据状态贴在不同的栏下面。
慢慢的物理的看板变成了电子看板,通过各种项目管理软件来管理跟踪这些任务,即时贴也变成了 Ticket(也有叫 Issue 的)。逐渐的,所有与开发相关的任务也都和 Ticket 挂钩了:
报一个 Bug,提交一个 Ticket ;
提一条需求,提交一个 Ticket ;
要重构一下代码,提交一个 Ticket 。
看板这种基于 Ticket 来管理跟踪任务的方式,看起来繁琐,但确实是很高效的一种方式。
每一个任务的状态都可以被跟踪起来:什么时候开始做的,谁在做,做完没有。
整个团队在做什么一目了然。
Ticket 和敏捷开发中的 Backlog(任务清单)正好结合起来,通过 Ticket 可以收集管理整个项目的 Backlog 和当前 Sprint(迭代)的 Backlog。
有了看板后,大家每天上班第一件事就是打开看板,看看当前 Sprint 还有哪些 Ticket 没有完成,哪些已经完成,哪些正在进行中,非常直观。
作为项目成员来说,做完手头的事情也不用去问项目经理该干什么事情了,直接从 To Do 栏选一条 Ticket 做就是了;对于项目经理,看看 To Do 栏还有多少没有被选取,就知道还剩多少 Ticket 没完成,看看 In Progress 栏就知道哪些 Ticket 正在进行中。
如果有 Ticket 在这一栏待太久或者这一栏 Ticket 太多,那可能就有风险了,就可以及时介入。
对于项目管理软件和 Ticket,我在后面章节中还会有进一步介绍。基于 Git 和 CI 的开发流程
如果你的团队应用瀑布模型来开发,大概会有两大烦恼:代码不稳定和部署太麻烦。
早些年虽然也用源代码管理,但是大家都是在 master(主干)上开发的,所以 master 的代码特别不稳定,一不小心就可能被人签入了不稳定的代码。所以在上线前,有一段时间叫“代码冻结期”,意思就是这期间,除非是紧急修复,否则谁都不能往上面提交代码。
还有测试环境的部署也是个老大难问题,尤其是服务一多,编译时要注意各种依赖,注意各种环境的配置。所以更新测试环境是个大工程,以至于当年我在飞信的时候,专门有人负责部署测试环境。
上面的“代码冻结”和“专人部署”方案,可一点都不敏捷。所以团队想要敏捷起来,一定要解决代码不稳定和部署太麻烦这两个大问题。
好在基于 Git 的开发流程结合 CI 的自动测试部署,很完美的解决了这两大问题。
Git 本来只是源代码管理工具,但是其强大的分支管理和灵活的权限控制,结合一定的开发流程,却可以帮助你很好的控制代码质量。
我们假设现在 master 的代码是稳定的,那么怎么保证新加入的代码也稳定呢?
答案就是代码审查(Code Review)和自动化测试。如果代码有严格的审查,并且所有自动化测试代码都能测试通过,那么可以认为代码质量是可靠的。当然前提是自动化测试代码要有一定的覆盖比率。
关于这点,对于大厂来说倒不是什么问题,正规的项目组对于代码审查和自动测试代码的覆盖率都有严格的要求。现在还有一个问题,就是如何在合并到 master 之前把代码审查和自动化测试做好呢?
简单来说,就是每次要往 master 添加内容,不是直接提交代码到 master,而是先基于当前稳定的 master,克隆一个 branch(分支)出来,基于 branch 去开发,开发完成后提交一个 PR(Pull Request,合并请求)。
PR 提交后,就可以清楚的看出来代码做了哪些改动,其他人就可以针对每一行代码写评论提出修改意见。如果确认代码没问题了,就可以通过代码审查。
接下来还剩下自动化测试的问题。这时候该 CI (持续集成)出场了。
如果你不了解 CI 是什么,可以把它想象成一个机器人,每次你提交一个 PR(严格来说是 Commit,这里略作简化)到源代码服务器,这个机器人马上就知道了。
然后它创建一个干净的运行环境,把你提交的代码下载下来,再下载安装所有依赖项,然后运行你的所有测试代码,运行完后,把测试结果报告给你。测试结果直观的反馈在 PR 上,绿色表示通过,红色表示不通过。
(图片来源:Video-React 项目 PR )
关于 Git 和 CI,我在之后的文章中会展开讲解,这里只是为了展现敏捷开发方法的流程。另外,阮一峰老师写过两篇文章,《Git 工作流程》《持续集成是什么?》,你也可以先行阅读了解。
至此,代码审查和自动测试的问题都解决了。当一个 PR 代码审查通过,以及 CI 通过了所有自动化测试,就可以合并到 master 了,而且我们也可以认为合并到 master 后的代码也是稳定的。
至于自动部署测试环境,反倒是简单,就是 CI 这个机器人,在你代码合并到 master 的时候,再次运行自动化测试代码,测试通过后直接运行自动部署的脚本,把 master 代码部署到开发环境或测试环境上。
在这里以一个开发任务为例,大致讲解一下应用敏捷开发方法的基本开发流程:
把要开发的 Ticket 从“To Do”栏移动到“In Progress”栏;
从主干(master)创建一个分支(branch),基于分支去开发功能或修复 Bug;
编写实现代码和测试代码(单元测试和集成测试),是不是测试驱动不重要,看个人偏好或团队要求;
持续提交代码更新到分支,直到完成;
创建 PR(Pull Request,合并请求),邀请其他人帮忙 Review 代码,根据 Review 的结果,可能还需要更新几次;
CI 在每一次提交代码到代码库后都会自动运行,运行后主要做这些工作:
– 检查代码格式是不是符合规范;
– 运行单元测试代码;
– 运行集成测试。
最终这些检查都完成后,CI 会把执行结果显示在 PR 上。通常绿色表示通过,红色表示失败;
PR 能合并需要满足两个条件:CI 变绿 + 代码 Review 通过;
PR 合并后,CI 会自动构建 Docker Image,将 Image 部署到开发环境;
将相应的 Ticket 从看板上的“In Progress”栏移动到“Done”栏。
(图片来源:Jira)
正常来讲,你是需要严格遵守开发流程的,但偶尔肯定也有紧急任务,来不及写测试代码,这种情况下,一定要再创建一条 Ticket 跟踪,以确保后续完成测试代码。
部署上线流程
最早的时候,程序员都是自己管服务器,但是由于这样过于随意,就会导致很多问题出现。
于是后来有专门的运维团队,将开发好的程序,编译好,数据生成脚本写好,然后写成部署文档,交给运维去手动部署。这个过程无比繁琐、无比慎重,通常几周才部署一次,遇上打补丁才隔几天部署。
这些年随着容器化、微服务、DevOps 这些技术或概念的兴起,部署已经变得越来越高效,大厂已经开始在部署流程上融合这些理念。
以前是运维人员按照文档部署,现在已经变成了 DevOps 写自动化部署工具,然后开发人员自己去部署生产环境。
现在大厂的部署也都实现了自动化,但是流程上还是有一些控制。
首先,部署的不再是程序代码,而是 Docker 的 Image,每次代码合并后 CI 都会自动生成新的 Image,测试也是基于 Image 测试。
部署生产环境之前,先在内部的测试环境充分测试。
部署生产环境前,需要审批确认,有 Ticket 跟踪。
部署时,先部署一部分,监测正常后再全量部署。
整个过程都有监控报警,出现问题及时回滚。
如果一切顺利的话,整个生产环境的服务部署过程通常几分钟就完成了,这在以前简直是不敢想象的事。
每日站立会议
在敏捷开发中,每日站会是非常有名的。在大厂,但凡实施敏捷开发的小组,上班第一件事,就是一起开一个站会,沟通一下项目的基本情况,这也导致会议室越发紧张起来。
虽然站立会议什么时间开都可以,但是早上无疑是最好的时机,一天工作的开始,开完会全身心去干活。
是不是站着开会其实不重要,重点是要高效沟通反馈。开会时间控制在半小时以内,半小时内不能完成的应该另外组织会议。
谁来主持站立会议呢?在敏捷的 Scrum 中,有一个角色叫 Scrum Master(敏捷教练、敏捷大师),主要任务就是保证各种敏捷流程的。
所以通常是由 Scrum Master 主持会议,也可以采用轮班制,每个星期换一名团队成员主持。负责主持会议的人,主要职责是组织会议,一个一个环节开展,控制好会议节奏。
开会都干什么呢?主要有三个话题:
一个应用敏捷开发的小组日常
这个小组是做网站开发的,基于微服务负责网站的某一个小模块。标准配置 7 人左右,4 个程序员(至少有一个资深程序员,有架构能力),1 个产品经理(Scrum 里面叫 Product Owner),1 个测试,1 个项目经理(Scrum 里面叫 Scrum Master)。主要负责网站某模块的日常维护。
这个小组是做网站开发的,基于微服务负责网站的某一个小模块。标准配置 7 人左右,4 个程序员(至少有一个资深程序员,有架构能力),1 个产品经理(Scrum 里面叫 Product Owner),1 个测试,1 个项目经理(Scrum 里面叫 Scrum Master)。主要负责网站某模块的日常维护。
在分工上:
产品经理:写需求设计文档,将需求整理成 Ticket,随时和项目成员沟通确认需求;
开发人员:每天从看板上按照优先级从高到低领取 Ticket,完成日常开发任务;
测试人员:测试已经部署到测试环境的程序,如果发现 Bug,提交 Ticket;
项目经理:保障日常工作流程正常执行,让团队成员可以专注工作,提供必要的帮助,解决问题。
在敏捷开发框架下,已经形成了一些很好的敏捷实践,这个小组也是基于 Scrum 方法做过程管理,基于极限编程做工程实践,看板可视化。每周一个 Sprint。
如何完成需求和修复 Bug?
这个小组的日常工作,也是围绕 Ticket 来开展的。所有的需求、Bug、任务都作为 Ticket 提交到项目的 Backlog,每个 Sprint 的任务都以看板的形式展现出来。
每个人手头事情忙完后,就可以去看板上的“To Do”栏,按照优先级从高到低选取新的 Ticket。选取后移动到“In Progress”栏。
每周一部署生产环境没有人愿意星期五部署,那意味着如果部署后发现故障,可能周末都没法好好休息了。所以即使程序早已经测试好了,除非特别紧急,否则都会留在下一周再部署。所以部署放在上半周,这样后面遇到问题还有足够的时间去应对。部署很简单,按照流程执行几个命令就可以完成生产环境部署。部署完成后,需要对线上监控的图表进行观察,如果有问题需要及时甄别,必要的话对部署进行回滚操作。但轻易不会打补丁马上重新上线,因为仓促之间的修复可能会导致更大的问题。像敏捷开发这样一周一个 Sprint 的好处之一就是,即使这一周的部署回滚了,下周再一起部署也不会有太大影响。每周二开迭代回顾会议,总结上个 Sprint每周二的早上,这个小组一般还会预留一个小时的时间,因为常规的站会完成后,还有一个迭代回顾会议 (Sprint Retrospective) 会议,目的是回顾一下在迭代中,团队有哪些做的好的地方,有哪些做的不好的地方。对于需要后续改进的,需要创建相应的 Ticket,加入到 Backlog 中,在后续迭代中改进完善。例如会议上,测试人员反馈说,上一个 Sprint,开发人员上线前几个小时还往预部署的分支里面更新代码,导致测试需要重新做回归测试,但因为时间不够了,没来得及测试完整,导致上线后不稳定,建议以后不要随意在上线前,在部署分支更新代码。对于这样的问题,可能不止一次发生,说明流程上还是存在问题。所以最后大家商定,以后如果不是紧急的修复,就不要在预部署的分支上更新,确实要加,需要和测试先确认。如果会议中要形成涉及项目的决策,最好是通过集体表决的方式决策,尽可能避免独裁式决策。因为敏捷的原则之一是要善于激励项目人员,给他们以所需要的环境和支持,并相信他们能够完成任务。
每周四迭代规划会,计划下周工作每周四早上,也需要一个小时来组织会议。因为常规站会完成后,还有一个迭代规划会(Sprint Planning Meeting)。这个会议是要大家一起讨论下一个 Sprint 的内容。在开会之前,产品经理和项目经理会商量好 Ticket 的优先级,会议上,大家一起按优先级从高到低的顺序,从 Backlog 中选出下个 Sprint 的内容。团队每个成员都要对候选的下个 Sprint Backlog 中的 Ticket 从 1-5 分进行打分,1 分表示容易 1 天以内可以完成的工作量,2 分表示 2 天内可以完成的工作,5 分表示非常复杂,需要 5 天以上的工作量。这里需要注意,打分时,要大家一起亮分,而不是挨个表态,不然结果很容易被前面亮分的人影响。
评估每条 Ticket 工作量的大概流程如下:会议组织者阅读一条 Ticket,可能是用户故事,可能是 Bug,可能是优化任务。同时会询问大家对内容有没有疑问。大家一起讨论这个 Ticket,确保充分理解这个 Ticket。每个团队成员在心中对 Ticket 进行工作量估算。会议组织者确认大家是否都已经确定估算结果,确认后,开始倒数:“3,2,1”,大家一起伸出一只手,亮出代表分数的手指头。如果估算结果存在分歧,出分最高的和最低的各自说明理由,讨论后达成一致。这种估算工作量的方法有个名字叫估算扑克,因为亮分时用扑克牌亮分而得名,但并非一定要用扑克牌。用这种方式评估工作量有几点很明显的好处:大家积极参与,详细了解需求。相比以前,可能只有当某个功能模块分配到自己头上的时候,才会去详细了解那部分需求,而其他开发人员可能都不了解这部分需求。工作量是由实际参与开发的成员作出评估,往往更准确也更容易被接受。以前项目经理代为估算的模式,很容易不准确,或者让开发人员抵触。促进成员的交流和经验分享。我们知道一般经验浅的新手估算工作量都会偏乐观,而经验丰富的老手则会更准确,通过这种方式,新手可以向老手学习到很多工作量估算甚至技术实现的经验。所以,在经过几个 Sprint 的磨合后,一般一个团队在每个 Sprint 的产出是比较稳定的。比如说这样一个 7 人的小团队,一个 Sprint 预计可以完成 20-30 分的 Ticket。
每周五分支切割周五标志着一周的工作要结束了,所以下班之前(4 点左右),要做 branch cut(分支切割),也就是要把当前主干上的代码,克隆到一个分支(branch)上。为什么要做分支切割这一步操作呢?经过一周的开发,master (主干)已经合并了不少新的 PR(Pull Request,合并请求),但是如果你直接把 master 的代码部署到生产环境,肯定还是不放心,毕竟自动化测试还是不能完全代替专业测试人员的测试。所以我们需要把 master 上的代码部署到测试环境进行测试,并且对测试出来的 Bug 进行修复,直到稳定下来为止。由于 master 还需要一直合并新的功能,所以最好的方式就是每次 Sprint 结束,从 master 创建一个分支版本出来,然后基于这个分支部署和修复 Bug。所以需要基于主干做一个 branch cut,创建一个预部署的分支,将预部署分支的代码部署到测试环境,这样在下周,测试人员就可以测试新的版本。测试验收通过后,预部署分支的代码会部署到生产环境。
每周轮值
小组里面除了日常开发工作以外,其实还有不少琐碎的事情,比如每周部署生产环境,每天部署测试环境,每周的 branch cut(分支切割),回答其他小组的问题,主持每日会议(不一定需要项目经理),这些事情如果都是一个人做难免会有些枯燥。在敏捷开发中,鼓励发挥每个成员的主动性,所以每周轮值是一个不错的方式,可以让每个人都有机会去体验一下,帮助团队完成这些事情,更有集体荣誉感和责任感。
一周一个迭代怎么保证质量?以前我在使用迭代模型开发时,一般是 4 周左右的迭代周期,2 周就是极限了,所以最开始看敏捷开发用 1 周的迭代周期,心中也有疑惑,1 周时间又要开发又要测试,怎么保证质量?实际实践下来,发现 1 周一个 Sprint 确实可行,而且质量也可以有保障,这里面有几个因素:(a) 有足够比例的自动化测试代码,可以很好地保证质量。当用户的主要功能都通过自动化测试覆盖时,基本可以保证主要功能流程不会出问题。(b) 一个 Sprint 开发完成后,并不马上部署生产环境,而是先部署到测试环境,会有 1 周时间测试。(c) 有专业的测试人员进行测试,并非完全依赖自动化测试。有时候一些大的功能更新,甚至会组织全组成员一起测试,以弥补测试人员不足的情况。在一个 Sprint 开发结束后,并不马上部署生产环境,而是先部署测试环境测试。
也就是说,虽然是 1 周的 Sprint,但是其实还有 1 周的时间进行测试。每个 Sprint 不仅开发新功能,还要同步修复以前版本的 Bug。这样基本上可以保证有好的质量。而且这种 1 周的迭代,可以保持每周都有内容更新,还有个好处就是每周更新的内容不多,出现问题的话,很容易就定位到是什么地方导致的问题。3. 基于敏捷开发如何做计划?大厂里面通常会在上一年底确定第二年整年的大的开发计划,并确定上线的时间范围,每个季度再根据情况做一些调整。这些大的计划最终会变成具体的开发任务,一个大的开发任务,会分拆到各个部门,各部门再将任务分拆到各个项目组。基于敏捷开发的话,主要就是看把这些开发任务放到哪几个 Sprint 去做,并且确保在规定的时间范围内完成。至于工期的估算,在迭代规划会上会对每个 Ticket 进行打分,根据分数可以预估有多少工作量,要花多少时间。
还有要注意一点,就是传统的项目经理,会是偏控制型角色,Scrum Master 则更多是一种服务型的角色,主要职责是保障敏捷流程的执行,以及提供必要的帮助,很多团队的决策就是采用集体决策的方式。另外,Scrum 有四种会议,除了前面介绍的三种:每日站会(Daily Scrum)、Sprint 计划会(Sprint Planning)和 Sprint 回顾会议(Sprint Retrospective),其实还有一种会议是 Sprint 评审会(Sprint Review)。Sprint 评审会的作用是让客户审查 Sprint 的完成结果。因为上面这个小组并没有直接的客户,都是完成产品经理提交的需求,而且沟通紧密,所以没有安排专门会议。这个小组的站立会议并不是“标准”的站立会议,Scrum 的站立会议通常只有 15 分钟,并且只有轮流发言环节。这里增加的每天审查 Ticket 环节,主要是为了将优先级高的 Bug 修复之类的 Ticket 放到当前 Sprint,及时响应,及时处理。有的项目组没有这个环节,是由测试人员或者 Scrum Master 直接将 Ticket 放到看板。这个小组并没有使用用户故事来开发需求,而是由产品经理事先写好需求文档。在上一篇文章里面,提到了 Scrum 采用用户故事的方式,分拆需求,减少繁重的需求文档,在实现的过程中再沟通确认需求。这是 Scrum 推荐的一种方式,也是一种高效的方式,但并不代表这是唯一的方式。如果有产品经理,可以提前几个 Sprint 就将需求文档写详细,一样可以达到高效的理解需求的效果。
1、我们最优先要做的是通过尽早的,可持续的交付有价值的软件来使客户满意
规划迭代故事时必须按照优先级安排,为客户先提供最有价值的功能。通过频繁迭代能与客户形成早期的良好合作,及时反馈提高产品质量。敏捷小组关注完成和交付具有用户价值的功能,而不是孤立的任务。以前我们都用需求规格说明书或者用例来编写详细的需求,敏捷使用用户故事来罗列需求。用户故事是一种表示需求的轻量级技术,它没有固定的形式和强制性的语法。但是有一些固定的形式可以用来参考还是比较有益的。敏捷估算中使用了这个模板:“作为【用户的类型】,我希望可以【能力】以便【业务价值】“。使用基于用户故事的需求分析方法时,仍可能需要原型和编写文档,只是工作重点更多的转移到了口头交流。
2、即使到了开发的后期,也欢迎改变需求。敏捷过程通过变化来为客户创造竞争优势。
这是一个关于态度的声明。敏捷过程的参与者不惧怕变化,他们认为改变需求是好的事情,因为那些改变意味着团队已经学到了很多如何满足市场需要的知识。
敏捷团队会非常努力的保持软件结构的灵活性,这样当需求变化时,对系统造成的影响是最小的。我们学习一些面向对象的设计原则和模式,这些内容会帮助我们维持这样灵活性。
3、经常性的交付可以工作的软件,交付的间隔可以从几周到几个月,交付的时间间隔越短越好。
经常性的交付可以工作的软件,这种做法就像一个人要减肥一样,减肥的时间间隔可以是几天,也可以是一周,在这个时间间隔内,均匀、并且努力的做运动,在饮食上也加以控制,这些运动和饮食上的控制必须是真实的、有效的,每个时间间隔可以观察一下自己的变化,通过不停的迭代,每个迭代都是有效的,几个周期下来就会看到自己的变化。软件也是具有同样的道理,我们每次交付的都是可以工作的软件,就算有什么问题,也可以及时修复,而且工作量也不是很大,对后面的迭代也不会产生多大的影响。如果我们按周期去交付,时间周期越很长的话,软件里面可能就会积累大量的功能,出问题的可能性也会增大。
交付时间间隔越短,粒度越细,而且每个阶段交付的都是可以工作的,客户就会看到软件的变化,也会提出他们的意见和建议,对于我们而言,修改和增加的成本也可以控制在一个很小的范围,同时也会增加客户的满意度。这个时间间隔要具体情况具体分析,制定符合自己团队的时间间隔。
4、在整个项目开发期间,业务人员和开发人员必须天天在一起工作。
为了能够以敏捷的方式进行项目的开发,客户,开发人员以及涉众之间就必须要进行有意义的、频繁的交互。只有大家在一起,才会有充分的沟通,才会有对系统的深入了解,对需求的更好的把握,对软件的交付也奠定了基础。如果 项目需求发生了变化,也可以更及时的响应变化,避免变化的延迟和项目的延期。
5、围绕被激励起来的个人来构建项目。给他们提供所需要的环境和支持,并且信任他们能完成工作。
在敏捷项目中,人被认为是项目中取得成功的最为重要的因素。所有其他的因素------过程、环境、管理等等---都被认为是次要的,并且当它们对于人有负面影响的时候,就要对它们进行改变。
例如:如果办公环境对团队的工作造成阻碍,就必须对办公环境进行改变。如果某些过程步骤对团队的工作造成阻碍,就必须对那些过程步骤进行改变。
6、在团队内部,最具有效果并且最具有效率传递信息的方法,就是面对面的交谈。
就像人谈恋爱一样,一定是面对面的谈,这样谈的才彻底,才会明明白白。在敏捷项目中,人们之间相互进行交谈。首要的沟通方式是交谈,也许会编写文档,但是不会企图在文档中包含所有的项目信息。敏捷团队不需要书面的规范、书面的规划或者书面的设计。团队成员可以去编写文档,如果对于这些文档的需求是迫切并且意义重大,但是文档不是默认的沟通方式。默认的沟通方式是交谈。
7、工作的软件是首要的进度度量标准。
工作的成绩不是看你加了多少班,来的多么早,走的多么晚,更不会看你帅不帅,而是看你做的软件的质量,软件的可以工作部分的总和是多少。敏捷项目通过度量当前软件满足客户需求的数量来度量开发的进度。它们不是根据所处的开发阶段、已经编写的文档的多少或者已经创建的基础结构代码的数量来度量开发进度的。只有当30%的必须功能可以工作的时候,才可以确定进度完成了30%。
8、敏捷过程提倡可持续的开发速度。责任人,开发者和用户应该保持一个长期的、恒定的开发速度。
敏捷项目不是50 米短跑;而是马拉松长跑。团队不是以全速启动并试图在项目开发期间维持那个速度;相反,他们以快速但是可持续的速度行进。
跑得过快会导致团队精力耗尽、出现短期行为以致于崩溃。敏捷团队会测量他们自己的速度。他们不允许自己过于疲惫。他们不会借用明天的精力来在今天多完成一点工作。他们工作在一个可以使在整个项目开发期间保持最高质量标准的速度上。
9、不断的关注优秀的技能和好的设计会增强敏捷能力。
高的产品质量是获取高的开发速度的关键。高的产品质量里面包含高的代码质量,良好的规范,结构稳定的架构设计。当有新的需求或者变化的时候,按软件设计原则去修改东西,或者说是增加东西,改动最少,牵扯最少,测试和维护成本都会降低。有这样的高品质的产品,才更容易面对变化,迎接变化。保持软件尽可能的简洁、健壮是快速开发软件的途径。因而,所有的敏捷团队成员都致力于只编写他们能够编写的最高质量的代码。他们不会制造混乱然后告诉自己等有更多的时间时再来清理它们。如果他们在今天制造了混乱,他们会在今天把混乱清理干净。
10、简单----使未完成的工作最大的艺术化-----是根本的。
敏捷团队不会试图去构建那些华而不实的系统,他们总是更愿意采用和目标一致的最简单的方法。他们并不看重对于明天会出现的问题的预测,也不会在今天就对那些问题进行防卫,避免过分设计,过分分析、过分架构。相反,他们在今天以最高的质量完成最简单的工作,深信如果在明天发生了问题,也会很容易进行处理。
11、最好的架构、需求和设计出之于自组织的团队。
任务不是从外部分配给单个团队成员,而是分配给整个团队,然后再由团队来确定完成任务的最好方法。
敏捷团队的成员共同来解决项目中所有方面的问题。每一个成员都具有项目中所有方面的参与权力。不存在单一的团队成员对系统构架、需求或者测试负责的情况。整个团队共同承担那些责任,每一个团队成员都能够影响它们。
团队不是一个人的团队,要共同解决问题,是分享解决方案,分享设计思路。团队成员也有角色分配,任务和责任,做自己适合做的事情,做对的事情。
12、每隔一定时间,团队会在如何才能更有效的工作方面反省,然后相应的对自己的行为调整。
敏捷团队会不断地对团队的组织方式、规则、规范、关系等进行调整。敏捷团队知道团队所处的环境在不断地变化,并且知道为了保持团队的敏捷性,就必须要随环境一起变化。