一个高效率的开发团队,具体的SCRUM实践如何高效开展从而保障工作效率呢?下面我们就从一个完整sprint的执行过程来分析高效率SCRUM开发的最佳实践:
至关重要的Sprint0
对于接受一个完整产品开发的团队,Sprint0的成果既是后续sprint的开发的基础,也是技术选型和总体设计的基础。所以Sprint0的质量对于整个产品的质量是至关重要的。项目可以根据项目规模的大小,安排足够的时间进行sprint0的开发,千万不要忽视sprint0的重要性。sprint0的成果必须是一个最小的可以运行的交付物,同时通过sprint0来验证总体设计技术选型是否有问题,技术架构是否合理。如果在sprint0的开发过程中,发现问题可以及时纠正,直到总体架构和技术路线完全确定,然后基于确定的技术路线和架构实现这个最小的runable的交付物作为后续sprint工作基础。这个sprint0的scope可以由总架构师和PO共同确定,各个开发人员参与进行具体的技术验证。
Sprint计划
项目开发之前,PO会把整个产品分解为UserStory或者epic,并大致安排好sprint。每个sprint开始前,PO必须落实下一个sprint的开发任务。在上一个sprint的后期,PO和ChiefArch根据当前sprint的完成进度和识别出来的问题,结合初始UserStory的sprint计划,确定下一个sprint开发哪些userstory,然后在sprintplanning的会议上以开发人员为主体对每个userstory进行任务分解,创建对应的sprinttask并估计工时。这个过程PO和Arch要仔细听取大家的意见,识别可能的blocker,有可能进行userstory计划调整。SCRUMmaster根据评估的工作量,计算整个sprint总的工作量,如果超出了capacity,则需要提出来移除一些userstory到下一个sprint。从实践上来看,建议周五开完sprintreview会议马上进行下一个sprint的planning会议,这样周一立即就可以开始下一个sprint的开发工作。
Task分解
Sprinttask的分解往往是从技术实现的角度进行分解,这是普遍采用的方式。但是如果团队主动性不强的情况下,为了保证工作效率,其实还有一个秘诀,就是在任务分解的时候适当增加开发人员之间的工作依赖。每一个开发人员都是有自尊心的,都不希望自己成为blocker,但是在scrum模式下,每一个开发任务都是同时接受了多个开发任务,每一个任务何时开始是自己决定的,这个时候很有可能出现开发人员甲先开发了一个任务,但是自己的部分开发差不多了,发现自己依赖的乙接受的开发任务还没有完成,此时就无法进行集成测试,通过这个适当的依赖的方式,开发人员甲会给开发人员乙带来一些无形的压力,使得乙会在完成自己的任务的时候更加快一下。如果没有依赖,很有可能开发人员甲自己完全控制了整个大的工作项的进度,如果甲工作积极性不高,那么他只要自己在sprint结束能完成就好了,会降低整个团队的工作效率。通过这个适当的依赖,利用peerpressure提高效率,使得sprinttask完成时间提前,这个时候可以给PM更多的时间进行功能测试,尽早的提出bug,可以在当前sprint修复bug提高这个sprint的开发质量。
工时估计
如果在sprintplanning会议的时候对于每一个task如何进行编码还没有一个大致的思路,这个时候进行工时估计十有八九是有偏差的。所以这种情况要尽量避免,预研工作可以作为一个task提前进行,然后在planning的时候,针对每一个task,大家一起梳理一下如何实现的思路,如果有遗漏的地方大家可以进行补充,这个时候进行工时的估计,就不会因为不确定性导致的偏差,也不会因为个人希望偷懒高估工时,因为做大家已经帮忙梳理过了。工时估计以承担这个任务的开发人员为主体给出估计,开发经理或者架构师可以进行适当的干预,但是干预要讲究策略,不要显露出不信任的态度,要帮忙分析难点在哪里,是否把需求想得太复杂了。
开发任务分配
开发任务的分配主要考虑任务的延续性,开发人员的技能和开发人员的可用工作时间几个因素。对于每一个任务,首先由各个开发任务进行主动的pick,这个时候开发人员会根据自己的知识是否能够承担这个任务以及自己的工作量进行挑选,同时开发人员也往往会选择和自己上一个sprint开发任务相关的工作,在开发任务主动pick的基础上,团队再适当进行协调就可以保证开发任务的适当分配。
sprint执行
经过sprinttask分解,工时估计和任务分配后,这个sprint的开发就进入了执行阶段,每一个开发任务根据自己的安排来完成自己的任务,同时如果依赖其他同事的开发,可以自行协调。每天的sprintdailymeeting同步自己的进度,分析遇到的问题和解决方法,并更新工作量燃尽图。sprintplanningmeeting要避免讨论具体的技术问题,因为这会浪费其他人的时间。SCRUMmaster要适当控制会议的时间,干预一不留神进行讨论某个技术问题的情况。但是dailymeeting可能会遇到的问题就是开发人员在工作中发现因为需求定义不清晰,或者前期预研工作不充分导致某项工作无法开展的情况,这时候要及时提出来,在大家集体讨论无法解决的前提下,需要及时调整sprinttask。也可能出现在开发过程中发现某项需求不合理,或者某个需求ROI不高的情况,这个时候也需要开发团队和PM进行沟通,调整sprinttask的情况。一般情况下,两周作为一个sprint,在第二周的周四上午需要完成全部的开发任务,周四下午和周五的上午主要进行一些质量收尾和deployment过程出现的问题修复。每一个sprint需要进行交付一个可执行的交付物。这个交付物不是单元测试和集成测试通过就符合要求的,还需要经过一些其他的qualitygate。比如周五的时候必须能将dev分支的开发merge到master分支,并将master分支的代码build通过后成功部署到集成测试环境。这中间会遇到各种问题:比如开发人员修改了表结构,导致部署到集成测试环境数据库访问出错,静态代码质量检查工作发现新的错误。
单元测试
单元测试是保证软件质量的一个必要的手段,但是还不是充分条件。单元测试的好处是发现一个开发人员没有意料到的问题,更重要的好处是当软件代码发生变更的时候能帮助开发人员发现代码变化没有考虑到的因素。单元测试对于一些内聚度高的类是比较容易的,只需要在测试代码中构造对象并调用对象的方法,然后比较调用的结果,利用第三方的单元测试工具包进行断言输出就可以。这类的单元测试唯一要考虑的因素是有一些private的方法必须通过public的方法进行间接调用进行测试,if逻辑判断需要编写不同的测试方法,protected方法需要将单元测试类声明为被测试类的子类从而获得prorected方法的访问权限。对于数据库访问的逻辑,可以通过sidecar的数据库dockerinstance进行测试,也可以通过mock的方式进行测试。建议通过sidecar数据库的方式进行测试,因为mock总是假定数据库的输出developer假设的值,但是这个假设不一定成立,而且不利于测试出一些边界条件,这些边界条件可能在实际生产环境会遇到。对于代码需要依赖其他api或者外部服务的情况,笔者建议写两套代码,一部分用于自动化的单元测试可以采用mock的机制,另外一套测试代码用于developer自己的集成测试,通过真正的服务或者api调用来测试。mock的方式进行单元测试总给人一种不踏实的感觉,因为mock基于假定。但是自动化的单元测试,如果不mock,则很可能因为外部服务偶尔的不稳定导致build不通过,中断整个自动化的集成和部署流程。笔者还观察到一个现象,当一个开发人员千辛万苦精疲力尽把某一个task的功能实现完毕,调试通过的时候,自然就会有所松懈,有了一种大功告成的感觉。此时立即让开发人员开始编写单元测试代码就会有些不情愿,更愿意把文档和单元测试的工作往后推一推。面对这样的问题,一方面我们要加强开发人员单元测试意识的培养,让大家认识到功能实现并且单元测试覆盖率达到目标才是真正的大功告成;另外也可以借鉴结对编程的思路,安排开发人员交叉编写单元测试代码,这样可以规避上述的懈怠心态导致的可能的消极怠工,同时也是为公司产品以后的维护培养了backup,一定某个同事休假需要维护的时候,有其他人也对这部分功能比较熟悉,能够很快接手。
软件漏洞和规范性检查扫描工具
软件漏洞和规范性检查扫描工具是软件质量保证一个很好的工具,这个工具不仅仅对于初级程序员帮助很大,即使对于很有经验的高级程序员,也有可能因为一时的疏忽导致一些问题。当然这些工具比较都是静态代码扫描工具,可能会给出一些falsenagative的诊断出来,这种情况下,研发管理上要允许通过高级程序员的代码审查,利用