Scrum 敏捷开发

Scrum 是当前最流行、有效的敏捷开发方法,它的具体框架如下:

  • scrum 开发框架中,整个开发过程是由若干个很短的迭代周期组成,短的迭代周期称为 sprint;
  • 首先产品经理根据用户需求和市场需要提出一个按照商业价值进行排序的客户需求列表,
  • 在每一个迭代的开始,开发团队要召开迭代计划会议,从这个列表中挑选出一些优先级最高的条目形成迭代任务,
  • 在迭代开发过程中,要进行每日例会,检查每日进展情况,
  • 在迭代结束时产出一个可运行可交付的软件,由项目的相关人员参加产品演示和回顾会议,来决定这个版本是否达到了发布的标准;

Sprint 的特点

  • 一个 Sprint 是一个 1-4 周的迭代,它是一个时间盒
  • Sprint 的长度一旦确定,将保持不变
  • Sprint 的产出是“完成”的、可用的、潜在可发布的产品增量
  • 产品需求在一个 Sprint 内是不允许变化的,除非发生了非常激烈的变化,但由于开发周期很短,这种情况发生的概率和影响都很小

Scrum 框架

Scrum 框架可分为三个方面:

1. 角色

  • 产品负责人 Product Owner
  • Scrum 主管 Scrum Manager
  • 团队成员 Team

Scrum 团队

自组织团队是敏捷软件开发的基本观念,即团队被授权自己管理他们的工作过程和进度,并且团队决定如何完成工作

  • 团队决定谁做什么,即任务的分配
  • 团队决定如何做,如何实现目标,即团队做技术决策
  • 团队需要在确保目标的前提下制定团队内的行为准则
  • 团队有义务保持过程的透明性
  • 团队监督和管理他们的过程和进度

2. 活动

Scrum 规划

  • 迭代计划会议 Sprint Planning

    • 在每次迭代(或冲刺)开始时召开,一般是 2~4 小时,目的是选择和估算本次迭代的工作项
    • 第一部分:以需求分析为主,选择和排序本次迭代需要实现的订单条目
    • 第二部分:以设计为主,由开发团队确定系统设计方案和工作内容
    • 所有团队成员都要参加,由产品负责人逐条讲解最重要的产品功能,开发团队共同来估算用户故事所需要的工作量,直到本次迭代的工作量达到饱和为止,产品负责人参与讨论、并回答与需求相关的问题,并不干涉估算的结果;
  • 每日站立会议 Daily Scrum Meeting

    • 会议目的:团队在会议中做计划,协调其每日活动,还可以报告和讨论遇到的障碍;任务板帮助团队聚焦于每日活动上,应在这个时候更新任务板和燃尽图;
    • 每个团队成员需要回答三个问题:上次例会后完成了什么?遇到了什么困难(或障碍)?下次例会前计划做什么?
    • 是内部成员的沟通会议,帮助大家快速地发现问题,促进团队的自组织和自管理;
  • 迭代评审会议 Sprint Review

    • 迭代结束时召开,开发团队和相关人员一起评审迭代产出的结果
    • 由团队展示有可能发布的产品增量,允许所有参与者尝试由团队展示的新功能,用户对团队演示的产品功能进行反馈
  • 迭代回顾会议 Sprint Retrospective

    • 每一次迭代完成后,都会举行一次迭代总结会,会上所有团队成员都要反思这个迭代
    • 举行迭代总结会议是为了进行持续过程改进,时间限制在 1 小时左右
    • 迭代回顾会议的关键要点:
      • 会议气氛:团队全员参加,气氛宽松自由,畅所欲言,发现问题和分析原因;
      • 关注重点:每次仅就 1-3 个关键问题做出可行的解决方案;
      • 跟踪闭环:可以放入下一次迭代订单中执行改进。

3. 制品

  • 产品订单 Product Backlog:

    • 产品订单是从客户价值角度理解的产品功能列表,功能、缺陷、增强等都可以是产品订单项,整体上从客户价值进行优先级排序
    • 在迭代计划时,产品负责人告诉开发团队需要完成产品订单中的哪些订单项,开发团队决定在下一次迭代中他们能够承诺完成多少订单项。在迭代的过程中,没有人能够变更迭代订单,这意味着在一次迭代中需求是被冻结的
    • 使用用户故事描述产品订单条目是非常有效的实践,用户故事是从用户角度来描述他所期望得到的功能:
  • 迭代订单 Sprint Backlog

    • 迭代订单是从开发技术角度理解的迭代开发任务
      • 简单环境:可直接把产品订单项分配到迭代中
      • 复杂环境:可把一个产品订单项分为 Web/后台……软件/硬件…… 程序/美工……等开发任务
    • 可工作软件是可交付的软件产品,“可交付”应视不同情况提前设定和选定交付标准,正式产品可能包括使用文档,在新产品开发初期可能只需要交付勉强看到效果的产品。
  • 燃尽图 Burndown Charts

    • 以图形化方式展现了剩余工作量(Y 轴)与时间(X 轴)的关系

任务白板

任务白板是团队开发的晴雨表,它将团队的任务和进度可视化地展现出来。 可以分为实物白板和电子白板,根据团队协作的场景不同,选择合适的白板。

用户故事与估算

用户故事(User Story)是从用户角度对功能的简要描述:

  • 常见的表达格式:

一个好的故事应当具备以下六个特点:

  1. 独立性:尽可能避免故事之间存在依赖关系,否则会使优先级和规划变得困难
  2. 可协商:故事是可协商的,不是必须实现的书面合同或者需求
  3. 有价值:确保每个故事对客户或者用户有价值的,最好是让用户编写故事
  4. 可估算:开发者应该能够预测故事的规模,以及编码实现所需要的时间
  5. 短小的:故事尽量短小,最好不超过 10 个理想人天,至少在一个迭代中完成
  6. 可测试:所编写的故事必须是可测试的,如此才能确认是可完成的

用户故事案例与类别

  1. 在使用信用卡进行网上购物时,可以创建这样的用户故事卡片:

    • 正面是对该故事的描述,反面是测试用例:
  2. 氪金游戏中排行榜:

    • 对所有用户都显示排名的原故事并不恰当,改进为显示部分高端用户的名次更有效:

用户故事总的可以分为两个类型:

  • 明确给出用户角色:
  • 用户角色比较抽象:

敏捷估算

需要多长时间才能完成特定版本的开发?做好项目估算非常重要,我们可以从每个故事的工作量入手:

  • 估算每个故事的工作量,然后汇总得到总工作量;
  • 然后根据工作速度的历史值,或者合理的推测,得到开发的平均速率(测算时只统计开发完成的迭代的速率)

敏捷估算的度量单位

敏捷估算有两种度量单位:

  • 故事点

    • 它是一个相对度量单位。使用时,可以给每个故事分配一个点值;点值本身并不重要,重要的是点值的相对大小
    • 故事点的基本做法:给一些简单的“标准故事”设定一个“标准点数”,形成比较基线;其他故事与标准故事进行比较,给出一个相对的比例,得到该故事的一个估计值。
    • 使用难点:故事点的项目或产品特征很明显,几乎无法进行跨团队比较;如果没有历史数据,很难设定标准故事
  • 理想时间

    • 它是一个绝对度量单位。理想时间是某件事在剔除所有外围活动以后所需的时间;一般为一天有效工作时间的 60-80% 比较合理,但绝不会是全部
    • 估算方法:团队查看每个故事,针对前面介绍的复杂性要素讨论故事,然后估计要用多少理想时间可以完成该故事
    • 这种方式是人们平时习惯使用的,容易理解和使用,但人们天生不擅长做绝对估计,很难达成共识,而且不同人的理想时间估算是不同的,因每个人的能力和认识的不同而不同,每个人的理想时间是不一样的,这种估算不能相加,由此产生的计划肯定是不准确的

敏捷估算的原则

  1. 开发团队一起估算,产品负责人和 Scrum 主管不参与实际估算

  2. 估算不是承诺,不应因其他因素而人工放大,成为团队与管理层之间的抛球游戏

  3. 估算应该准确但不必过分精确,过于精确的估算是浪费

  4. 应该使用相对大小而不是绝对大小进行估算,因为人们更擅长相对的估算

敏捷估算扑克

敏捷估算扑克本质上是扑克牌,它基于 Delphi 估算原理,可以快速地估算出需要的数字。估算扑克是一种基于共识的估算工作量的技术,估算扑克牌的数值范围,由团队决定——有些牌是自然数排列,有些是斐波纳契数,有些则是不连续自然数,例如 2 的幂。

具体步骤如下:

  1. 分牌:每名参与估算的成员分得相同花色的一组牌,两张Joker不参与估算

  2. 讲解订单故事:

    • 产品负责人从 Backlog 中选择一个条目,为大家详细讲解该条目;
    • 团队成员进行讨论并提问,产品负责人逐一解答大家的问题
  3. 进行估算:

    • 当团队成员确认已经对该条目完全了解且无任何重大问题后,大家开始进行估算,同时选出代表自己估算值的纸牌,在所有成员选牌完毕后大家同时亮牌
  4. 讨论:

    • 若每张牌估算值差距明显,代表大家对该条目没有获得共识,需要对评估结果进行讨论
  5. 达成共识:

    • 对该条目重新进行估算,直到团队的评估数值达成一致
    • 一般情况下,最多三轮就可以得出一个比较统一的意见;如果三轮之后依然没有得到统一的意见,那么 Scrum 主管 应立即中断估算 ,取平均值或其他大家接受的值作为估算结果。

团队协作工具 Tower

配置管理

软件开发中常常会遇到各式各样的问题:

  • 找不到某个文件的历史版本
  • 开发人员使用错误的版本修改程序
  • 开发人员未经授权修改代码或文档
  • 人员流动,交接工作不彻底
  • 无法重新编译某个历史版本
  • 因为协同开发或异地开发,版本变更混乱

因此非常有必要进行软件配置管理——软件配置管理是一种标识、组织和控制修改的技术,它作用于整个软件生命周期,其目的是使错误达到最小并最有效地提高生产率。软件配置管理的作用是:

  1. 记录软件产品的演化过程
  2. 确保开发人员在软件生命周期的每一个阶段 都可以获得精确的产品配置
  3. 保证软件产品的完整性、一致性和可追溯性

设计概念

  1. 软件配置项(Software Configuration Item,简称 SCI)

    • 是为了配置管理而作为单独实体处理的一个工作产品或软件
  2. 版本

    • 版本是在明确定义的时间点上某个配置项的状态;
    • 版本管理是对系统不同的版本进行标识和跟踪的过程,从而保证软件技术状态的一致性;
  3. 基线

    • Baseline 是软件配置项的一个稳定版本,它是进一步开发的基础,只有通过正式的变更控制过程才能改变
    • 每个迭代阶段都可以分为五步:

版本控制

团队开发时,经常会遇到版本控制相关的问题,比如以下两个场景:

  1. 场景 1:每个程序员各自负责不同的专门模块,没有出现两个程序员修改同一个代码文件的问题

  2. 场景 2: 假设两个程序员同时修改同一个代码文件,就会出现代码覆盖问题

为了解决覆盖、冲突的问题,通常有两种策略:

  1. 独占工作模式:即对代码库上锁后才能修改,未获得锁则不得修改

  2. 并行工作模式:复制代码库各自进行工作,合并代码库时检查是否有最新版本:

    • 并行工作模式的关键思路就是分支:
    • 分支包含了一个项目的文件树及其发展的历史,记录了一个配置项的发展过程。一个配置项可能选择多个分支,归并是将对分支的修改合并到另一个分支。

软件配置管理工具

Git

3. Git概念梳理及基本操作

Git工作区、暂存区、版本库的概念:

  • 工作区:电脑中能看到的文件夹下非隐藏的文件或子目录
  • 暂存区:存放在.git目录下的index(.git/index)文件中,亦可称之为索引
  • 版本库:工作区的隐藏目录.git

  • 图中左侧为工作区,右侧为版本库。在版本库中标记为 “index” 的区域是暂存区(stage/index),标记为 “master” 的是 master 分支所代表的目录树。
  • 图中我们可以看出此时 “HEAD” 实际是指向 master 分支的一个”游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
  • 图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。
  • 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
  • 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
  • 当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
  • 当执行 git rm --cached <file>命令时,会直接从暂存区删除文件,工作区则不做出改变。
  • 当执行 git checkout . 或者 git checkout -- <file> 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区中的改动。
  • 当执行 git checkout HEAD . 或者 git checkout HEAD <file> 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

3.1 配置

3.1.1 设置用户签名

git config --global user.name
 
git config --global user.emai

在任何一个地方打开git bash,输入以上两条命令设置全局签名。签名的作用是区分不同操作者的身份,而这个签名信息在每个版本的提交信息中都可以查看,以此区分本次提交来自于谁。如果去掉--global参数则只对当前仓库生效。

签名信息可以在当前用户家目录下查看:~/.gitconfig获知。

注意:这里设置的用户签名和之后登录GitHub的账号没有任何联系,仅作为提交本地代码时的签名。

3.1.2 其他git的配置命令

  1. 显示当前git的配置信息:git config --list
  2. 编辑git配置文件夹:
# 针对当前仓库
git config -e
 
# 针对系统上所有仓库
git config -e --global

3.2 基本操作及常用命令一览

 git help  
用法:git [-v | --version] [--help] [-C <路径>] [-c <名称>=<取值>]  
          [--exec-path[=<路径>]] [--html-path] [--man-path] [--info-path]  
          [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]  
          [--git-dir=<路径>] [--work-tree=<路径>] [--namespace=<名称>]  
          [--super-prefix=<路径>] [--config-env=<名称>=<环境变量>]  
          <命令> [<参数>]  
  
这些是各种场合常见的 Git 命令:  
  
开始一个工作区(参见:git help tutorial)  
  clone     克隆仓库到一个新目录  
  init      创建一个空的 Git 仓库或重新初始化一个已存在的仓库  
  
在当前变更上工作(参见:git help everyday)  
  add       添加文件内容至索引  
  mv        移动或重命名一个文件、目录或符号链接  
  restore   恢复工作区文件  
  rm        从工作区和索引中删除文件  
  
检查历史和状态(参见:git help revisions)  
  bisect    通过二分查找定位引入 bug 的提交  
  diff      显示提交之间、提交和工作区之间等的差异  
  grep      输出和模式匹配的行  
  log       显示提交日志  
  show      显示各种类型的对象  
  status    显示工作区状态  
  
扩展、标记和调校您的历史记录  
  branch    列出、创建或删除分支  
  commit    记录变更到仓库  
  merge     合并两个或更多开发历史  
  rebase    在另一个分支上重新应用提交  
  reset     重置当前 HEAD 到指定状态  
  switch    切换分支  
  tag       创建、列出、删除或校验一个 GPG 签名的标签对象  
  
协同(参见:git help workflows)  
  fetch     从另外一个仓库下载对象和引用  
  pull      获取并整合另外的仓库或一个本地分支  
  push      更新远程引用和相关的对象  
  
命令 'git help -a' 'git help -g' 显示可用的子命令和一些概念帮助。  
查看 'git help <命令>' 'git help <概念>' 以获取给定子命令或概念的  
帮助。  
有关系统的概述,查看 'git help git'。

3.3 初始化本地库

git init

在本目录下新建一个空文件夹 gitTest,打开文件夹并右键打开git bash,使用git init初始化;

此时,该目录中就会出现 .git 文件夹,查看其中文件:

简要讲解一下这里面文件及目录的作用:

  • 一个叫HEAD的文件,里面内容是ref:refs/heads.master,包含了一个索引信息,并且此索引总是指向项目中的当前开发分支;
  • 一个叫objects的子目录,包含了项目中的所有对象,我们不必直接地了解到这些对象内容,我们应该关心的是存放在这些对象中的项目的数据;
  • 一个叫refs的子目录,用来保存指向对象的索引。具体地说,子目录refs包含着两个子目录heads和tags,它们存放了不同的开发分支的头索引,或者是你用来标定版本的标签的索引。要注意的是,master是默认的分支,这也是为什么.git/HEAD创建的时候就指向master的原因,尽管目前它其实并不存在。git将假设你会在master上开始并展开你以后的工作,除非你创建你自己分支。另外,这是一个约定俗成的习惯,换言实际上可以将工作分支命名为任何名字,而不是一定叫做master。

3.2 查看本地库状态

git status

3.2.1 首次查看

此时工作区(存放代码的存储设备区域)没有任何文件:

3.2.2 新增一个文件并再次查看

在其中随便写点什么;保存退出后再查看一下:

注意到,第三行出现变化,显示”Untracked files: “,表示有文件未被追踪;

其中,hello git.txt 文件被标记为红色,这表示文件被保存在工作区,还未添加到暂存区;

3.4 添加暂存区

git add file.name

将 hello git.txt 添加到暂存区:

警告中 LF 表示Linux中的换行符,而 CRLF 是Windows中的换行符。

之后,git status的结果中原 hello git.txt 文件标记成绿色,这表示该文件已存放到暂存区,等待被提交(committed)。

其中,(use "git rm --cached \<file>..." to unstage)表示将文件从暂存区删除,但不删除工作区文件:

3.5 提交本地库

git commit -m "journal info" file.name

将暂存区文件提交到本地库:

其中,c6c559c是提交的版本号,通过git status查看状态信息,了解当前无新文件需要提交。git refloggit log是两个查看日志信息的命令,前者是查看版本,后者是查看版本详细信息。

注意,log 信息中版本号是完整的,而之前所说及 reflog 信息中的版本号是其前七位。

git commit 的提交信息使用 "" or ''?

在 Linux 系统中,commit 信息使用单引号 ‘msg’ ;在Windows 系统中,commit 信息使用双引号 “msg”。 而在 git bash 中 git commit -m ‘commit message’ 这样是可以的;在 Windows 命令行中就要使用双引号 git commit -m “commit message”。

3.6 修改文件

这时,再查看 status 信息,获知 hello git.txt 文件又被标记为红色,这表明改动后文件被保存在工作区,还未添加到暂存区;

之后可以通过 3.5节 中的流程重新执行一遍,将改动文件提交到本地库:

再查看提交状态及日志信息:

可以看到,reflog 中 HEAD 指针指向当前提交版本——f3a2ce5;

3.7 历史版本

3.7.1 查看历史版本

git reflog

git log

  • 可以添加--online选项查看简洁版本
  • 使用--graph以拓扑图形式查看历史中什么时候出现了分支、合并
  • --reverse逆向显示所有日志,
  • --author=coder-name可以查看指定提交者的日志

git blame <file>

3.7.2 版本穿梭

git reset --hard version.num

同时,查看工作区文件:

当然,能向前回滚到历史版本,也能再恢复到最新版本。

3.8 标签

摘自菜鸟教程

git tag

如果你达到一个重要的阶段,并希望永远记住那个特别的提交快照,你可以使用 git tag 给它打上标签。

比如说,我们想为我们的 runoob 项目发布一个”1.0”版本。 我们可以用 git tag -a v1.0 命令给最新一次提交打上(HEAD)“v1.0”的标签。

-a 选项意为”创建一个带注解的标签”。 不用 -a 选项也可以执行的,但它不会记录这标签是啥时候打的,谁打的,也不会让你添加个标签的注解。所以推荐一直创建带注解的标签。

$ git tag -a v1.0 

当你执行 git tag -a 命令时,Git 会打开你的编辑器,让你写一句标签注解,就像commit 写的注解一样。

现在,注意当我们执行 git log —decorate 时,我们可以看到我们的标签了:

*   d5e9fc2 (HEAD -> master) Merge branch 'change_site'
|\  
| * 7774248 (change_site) changed the runoob.php
* | c68142b 修改代码
|/  
* c1501a2 removed test.txt、add runoob.php
* 3e92c19 add test.txt
* 3b58100 第一次版本提交

如果我们忘了给某个提交打标签,又将它发布了,我们可以给它追加标签。 例如,假设我们发布了提交 85fc7e7(上面实例最后一行),但是那时候忘了给它打标签。 我们现在也可以:

$ git tag -a v0.9 85fc7e7
$ git log --oneline --decorate --graph
*   d5e9fc2 (HEAD -> master) Merge branch 'change_site'
|\  
| * 7774248 (change_site) changed the runoob.php
* | c68142b 修改代码
|/  
* c1501a2 removed test.txt、add runoob.php
* 3e92c19 add test.txt
* 3b58100 (tag: v0.9) 第一次版本提交

如果我们要查看所有标签可以使用以下命令:

$ git tag
v0.9
v1.0

指定标签信息命令:

git tag -a <tagname> -m "runoob.com标签"

PGP签名标签命令:

git tag -s <tagname> -m "runoob.com标签"
Link to original

持续集成与交付

Practice

选择

  1. 在敏捷开发方法中,用户故事(User Story)的作用是( )。 A. 定义需要发布给最终用户的软件特性和功能 B. 确定发布每一次增量的日程表 C. 用于代替详细的活动计划 D. 用于估算构建当前增量所需要的努力 E. 选项 A 和 C F. 选项 A 和 D

F

Git 操作

  1. 提交后,你才发现不小心把一些临时文件(*.xxx)也提交进去了,以下最好的解决方式是( )。 A. 编辑 .gitignore 文件,增加 *.xxx 条目,然后 git commit -a 把 .gitignore 提交到版本库 B. git rm 删除*.xxx,然后 git commit 提交 C. git rm 删除*.xxx 后再编辑.gitignore 增加*.xxx 条目,最后 git commit —amend 进行修补提交 D. 幸好还没进行 git push,重新 clone 然后重新添加文件后提交即可

C