SCMLife.com

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 2477|回复: 1

[赏析] 流水先生新作《软件集成策略》赏析八

[复制链接]
发表于 2013-9-13 18:32:16 | 显示全部楼层 |阅读模式
3.5 从虫子的视角看集成
. g6 j, u' l, H& b

! l( |. V8 ^% {! J3 z. i0 c& J
本节的标题中,虫子是指 bug,即软件中的缺陷(defect)。事实上,本节将不止讨论程序运行时显示的缺陷,也包括编译构建时暴露出的问题,以及代码合并操作时遇到的冲突。我们将从所有这些问题的视角看项目的运转。为什么要从这个角度看?
9 S: z- F! `# A; \" E/ n, [$ W
如果没有这些问题,集成是一件特别简单轻松的事情。不再需要系统测试团队,因为没有问题可供发现。集成工程师也没什么事情干了,没必要经常产生基线,因为测试团队消失了,他们不需要基线了;而程序员也无此需求——从集成分支末端就可以直接拿到高质量的软件。程序员也不再需要在开发过程中和提交前进行各种检测工作,因为没有问题了嘛!
) z+ S- {) A( v: R5 W
, M" |& W- d% h% `' S4 f: V
在这样的情景中,集成变成了“机械性”的工作,单纯地把代码及其修改变化搬运来搬运去;把不同的部分、不同的改动放到一起;必要时打个标签标识一下发布的版本,等等。在这样的情境里,项目中只有一种流动:通过开发和集成活动,开发成果不断汇聚到一起,程序功能越来越多、越来越强。
2 s3 f. V+ G) Q4 A7 h8 k/ t
在真实世界中,软件开发项目中不仅上述一种流动,还有另一种流动,问题的流动。随着程序功能的增强,问题也不断产生。问题被裹挟在代码中,随着代码的流动而流动,从一个人的工作区,到对多个人产生影响,甚至在发布后对广大使用者产生影响。好在,在流动的过程中,问题不断的被发现,不断的被解决。以至于到发布时,程序达到可以被使用者接受的质量。

( T7 j% I* T: Q8 C+ y之所以在真实世界中,需要系统测试团队的大量集成工作,需要集成工程师的大量集成工作,需要程序员在开发新功能外还要做大量集成工作,是因为代码里面不断地产生问题,而这些问题需要被消灭。既然是因为软件中裹挟着问题才需要这么多的集成工作,是这些问题的数量和性质决定了要做什么集成工作、何时做、做到什么程度;是这些问题决定了为此要占用多少资源、耗费多少成本、增加多少项目时长;是这些问题决定了集成相关的策略,那么让我们从问题的视角来分析整个情景,看看问题对项目各因素的影响。: n8 r& ^" r: w% b4 m! |# L

' A8 B  v# g& U% Y2 m
在研发过程中,问题不断产生出来。第 1.6节给出了问题的分类。要想让集成省事轻松代价低,如何减少各类问题的产生是一个思考的方向,比如频繁的代码同步能减少隔阂产生的问题。当然,减少各类问题本身,也需要付出代价。比如为了减少增量编译不可靠而造成的问题,打算改用全量编译,那么编译时间就会大大延长。这需要综合考虑。
/ W2 P) d' r2 b  O3 h
在问题产生出来后,如果放任问题不管,那么问题不断累积起来,其数量通常会远远多于将来程序使用者能够接受的程度。也就是说,无法满足软件发布时的质量要求。为了达到软件发布时的质量要求,我们不得不进行检测以发现问题。' F6 n% I" y, O+ n9 K

/ u; g& g9 l& G( d% o" D
光发现问题还不够。在问题被检测出来后,或者问题被不经意遇到后,要进行修复。问题可能是发现者自己修复的,比如程序员在提交前修复他发现的本次修改引入的问题。也可能是其他人修复的。比如测试人员发现的问题,在记入缺陷跟踪系统后,由程序员修复;再如集成工程师发现问题后,可能把有问题的提交从本次狭义集成中剔除出去,等程序员慢慢修复。
) g$ t2 t. k6 S# O% Y) ?0 n; w: ^
当然,为了能够不断检测出并且修复问题,以达到项目要求,我们需要有投入,有代价。这意味着需要资源、消耗成本,这也意味着也增加项目时长。比如,集成工程师必须等待程序员完成检测和修复后,提交改动到集成分支,才能进行狭义集成工作,而测试团队和开发团队在等待集成工程师完成检测和修复后发布基线,才能测试它或者基于它继续开发。这样一环套一环,于是影响了项目时长。如图3-8所示。* ]  [7 e5 d# Y( L$ S
' E2 X- g1 h% ]+ d6 m6 p+ w
为了发现问题和解决问题,需要付出代价。用不同的检测方法、在集成的不同阶段发现问题,其发现问题的代价不同。在集成的不同阶段解决问题,其代价也不同。我们要思考,如何合理安排检测和修复工作,选取合适的方法,选取合适的时机,尽量降低投入,减少对项目时长的影响,以降低发现问题和解决问题的代价,降低集成的代价。
4 [6 g7 \1 A- u% U  @) R6 I
比如,适当地把检测和修复工作与功能开发工作并行起来,而不是拖到功能开发工作完成后,有利于减少集成工作对项目时长的影响。当代的集成基本策略是,早一点发现问题,早一点修复问题,不要累积,防止项目后期问题堆积如山。6 i$ A, [* o2 f! A  V: f

" D+ I, |1 G5 e: c9 y6 [
问题对项目的影响不仅体现为,为满足项目发布的质量而进行检测和修复工作,并因此而付出代价。软件中存在问题还会影响软件开发本身。作为一个极端的例子,如果一个编译问题存在于软件的基线中,并且同时有很多程序员拿到了这个基线,打算基于它继续开发工作,那么他们就全中枪了。在这个例子中,项目为软件中的问题付出代价:集体被打扰、受困惑,进行分析确认、沟通交流,或许还要因此而等待。这些代价转换为项目六要素中的资源的占用,成本的增加和项目时长的增加。
& v8 f" E* e4 R. _- I
与此相关的,软件中的问题相互作用,可能会造成调试和解决问题的不便。错误之间互相遮掩,互相影响,你看到的表象实际上是多个错误相叠加的结果。你试着改正了一个错误,可是看到的表象仍然是有问题的,没有改变。于是你就会怀疑,是否真的改正了一个错误。这种困扰,同样是持有问题的代价。9 D+ N& }2 _' i

+ l* t9 J+ k$ L, A  i
而比这更严重的,是以缺陷的形式体现出来的,本质上是程序架构不良。基础没打好,如何起高楼?将来再修正,难度很大,代价很高。这样的情况,同样是持有问题不及早修正造成的。

: c# K' J) g* p' ?- E0 _不仅程序员需要有一定质量保证的程序,以便继续开发,测试团队也需要有基本质量的软件,才能开展全面深入细致的测试工作。如果软件已启动就崩溃,那就没办法测试,空占资源、虚耗成本、拖延项目时长。也就是说,必须先发现并修复了基本问题,才能继续检测,发现其他问题。如图3-9所示。  o5 Z  U* X; b* A

$ o% p" ?0 e! N
  s9 f' U; b3 `- a  ]
为了减少这样的情况,需要在适当的时候进行检测和修复工作,减少程序中的问题,特别是严重问题,向程序员和测试人员提供有一定质量保证的软件,以便继续工作。但是如我们前面所说,检测和修复工作本身,同样有代价:资源、成本和项目时长。既然受打扰要付出代价,为避免受打扰而预先采取行动也要付出代价,那么就需要仔细权衡,寻找昀合适的集成策略。

- ?; n' [( h$ T$ H: x3 c总之,在软件开发过程中,我们要减少程序中的问题,这既是因为昀终要满足发布的质量要求,满足项目要求;也是因为软件开发过程中持有问题,会干扰开发和检测,要付出代价。为了减少程序中的问题,我们有两个方法:我们要减少问题的产生,也要消灭已产生的问题。然而,减少问题的产生也要付出代价;为消灭问题而进行的检测和修复活动本身也有代价①。因此,制定集成策略,是一项平衡的艺术。其目标是降低集成相关的综合代价。也就是说,主要就是要有效率地提升质量,即本书副标题。如图3-10、图 3-11所示。6 r: W+ `1 \1 v2 u

* q- f7 v! s" K- |0 w

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
 楼主| 发表于 2013-9-13 20:12:21 | 显示全部楼层
3.6 从不同的视角看虫子
# @; q, ~0 Q" }; ?; r! v

- E" W" \: h: X2 S5 F, ~0 r7 A5 v
第一个视角,从影响昀终使用者的角度看,有的问题很需要修复,有的问题不太需要修复。这主要和两个因素有关系:一方面,使用者每次遇到这个问题时,它会造成多大的危害和影响。这大致对应于常说的问题的严重性。另一方面,使用者会多么容易多么经常地遇到这个问题。这两方面因素,是类似于相乘的关系。两方面因素的值都高,那么这个问题的修复价值就高。两方面因素都低,那么这个问题的修复价值就低。

. h! {  f% u: X: |7 A) B* L' K4 q软件发布时的质量要求,大致上意味着所有已发现的和存在但尚未发现的问题,其修复价值的累积,不能超过一定的值。当然,作为极端情况,有些严重问题是不可容忍的,一个就足够召回已出售的产品了。

" Q4 c" c1 _. q' E- B& n3 C3 L" `* q' P
一般来说,产生问题后,早点发现,早点修复比较好。因为趁着记忆还新鲜,改起来快,而且流程也相对简单。本节末会详细解释这一点。但这并不意味着这是火烧眉毛的事,并不意味着产生问题后,片刻都不能耽误,必须马上修复,或者必须在提交前修复,或者必须在产生基线前修复。没急到这个程度,稍晚一点修复也没关系:提交不是发布,基线也不是发布,提交里的问题和基线里的问题,并不会影响将来发布后用户使用。
6 [" F/ J; O& v: `5 r8 H3 a" _/ K5 ~
第二个视角,从影响开发和集成工作的角度看,有的问题很需要修复,有的问题不太需要修复。这主要是看这个问题挡住了多少后续功能的展现。比如,如果一个功能的主界面都进不去,就难以开发和测试它的具体内容,因而这个问题就很需要修复。4 B" R6 k/ o2 J  f- Y7 }

" n/ C/ `% c8 B$ ^: ?3 z
一个问题在多大程度上影响开发和集成工作,除了看它本身的性质外,还要看它流传到了多少人那里:是仍在某个程序员的工作区呢,还是流传到了很多程序员那里,陆陆续续“引爆”。此外,还要看它存在了多长时间。问题产生后立刻修复,还没几个人碰到,每个人还没怎么因为它耽搁时间耽误事情,与问题产生后好几天才修复,大家纷纷碰到,每个人因为它耽搁不少时间不少事情,显然是不同的。

, ~+ U: j- a( _从这个视角看,对于像编译构建不通过或者程序主要功能无法启动这样的严重问题,那可就是火烧眉毛的事了。这样的问题要是扩散开去,会给大家带来严重的伤害。所以,程序员尽可能不要把这种问题提交上去,集成工程师也要严防死守。万一这类严重问题,或者比较严重的问题,还是出现在了基线里,那就要尽早发现,并把修复它当做昀高优先级的事情。; J4 A. ^' {7 ^7 z* }

2 `( L% S: m. B5 n3 @
从这个视角看,那些很少出现,或者出现了也对程序员和测试人员的工作没什么阻碍,无足轻重的问题,就不紧急了。没必要特别关注它们,没必要采取什么特别行动,只要等正常的检测流程发现它们,然后再修复就是了。而如果在正常的检测流程发现它们之前,有极个别程序员不幸撞上它们,那也没什么大不了的,甚至是个好事儿,早点发现了,就早点修复呗。

% o: o+ s7 o. \0 ]第一个视角和第二个视角,分别是从外部使用者和内部开发者的角度看一个问题的修复价值。这两者在概念上是不同的。比如,程序主界面上的一个明显的错别字,给使用者的印象会很不好,但对内部开发者的阻碍并不大。不过两者有一定的相关性。在统计上,对内部开发者来说修复价值大的问题,常常对外部使用者来说修复价值也大。这是因为,阻碍很多后续功能展现,严重影响开发者的问题,通常也是外部使用者容易遇到并且影响很大的问题。比如说,不能进入某个重要功能的主界面这个问题,使得用户无法使用该功能,也使得程序员难以开发该功能、测试人员难以测试该功能。; _' i9 [* K8 z3 w

( m& ]0 ?  b( R/ ]
我们平时说问题的严重程度,主要是在讨论一个问题的对外修复价值,但同时也在一定程度上反映出该问题的对内修复价值。我们平时所说的问题的紧急程度,主要是在讨论一个问题是否要在昀近的一次对外发布前修复,但有时也是指因为严重干扰了开发和测试而需要紧急修复,或者是这两方面因素综合考虑的结果。
第一个视角和第二个视角,是在讨论一个问题的破坏性。而接下来的第三个视角,是从检测工作的角度看。有的问题容易发现,容易检测出来,稍微测测就暴露出来。有的问题不容易检测出来,躲在边边角角,需要花大力气。
+ ^8 j* |1 z+ ~1 P
这个视角,与前两个视角也有相关性。程序的外部使用者容易遇到的问题,常常也是内部检测时容易找到的问题;阻碍后续功能展现,因而干扰开发和检测的问题,常常也是检测时能轻易发现的问题。换句话说,在统计意义上,严重的问题容易发现。如图3-12所示。当然,这只是平均下来的统计结果,可以轻易找到反例。
2 ]! ]7 s# o4 n6 G# J& h4 @- {3 {# z/ i% N( W) Y$ g. g1 c
3 {% a- g9 w9 [7 Q" o' L+ x% C
是否容易发现问题,也就是发现一个问题所需要付出的代价,除了取决于这个问题本身,还取决于用什么样的方法找到它。同样一个新引入的编译问题,用全量构建找到它,就慢。用增量构建找到它,就快。类似的,发现一个源代码中的缺陷,用同行评审、用单元测试、用系统测试,需要不同的时间,不同的代价。
  H: F+ j$ ~0 O/ K
从发现问题的方法上看,自动化的检测特别是自动化测试,开发相关脚本这样的一次性投入可能比较高,但是每运行一次的代价,跟人工测试相比就很低。因此从长期来看,往往很有优势。更进一步,它使得测试频率得以大幅度提高,于是大大加快了反馈的速度,于是带来更多好处。持续集成就是建立在这一系列自动化的基础之上的。
1 E  _: k8 Q/ z/ u; {1 c
我们继续讨论第三个视角,发现一个问题的代价。每发现一个问题的代价,还取决于当时软件(某个区域)中(这类)问题的密度。一般来说,问题越少越稀疏,平均发现每个问题的成本也就越高。如图3-13所示。
# L- ?% g! ]1 D4 O) [" M8 }) |0 F4 M, ?5 d
0 W/ W2 Y. v) Y9 p1 t
这就好像撒网捕鱼,同样是一网撒下去,该水域的鱼越稀疏,捕上来的越少,平均每条鱼的捕捞成本就越高。

- M! W- g# ]6 y! e这反映了经济学上的边际效用递减规律。对程序进行检测,开始的时候,在很短的时间里,就能发现大把大把的缺陷,而且充斥着相对来说比较严重的缺陷。等好找的都找出来了,再接着找不好找的,那可就费劲儿了。每份投入,发现的问题越来越少,越来越轻微。
" F9 ^* j$ _/ d8 W5 W  y/ u/ \/ W0 S5 E
注意,考虑这个规律的影响时,还要考虑第二个视角中谈到的规律的影响。严重问题的存在,会阻碍继续发现其他问题。因此,必须等待严重问题被处理后,才能继续“前进”。这种等待,有时候代价很大,比如 100个测试工程师都被程序不能启动这个问题阻碍住的时候。
3 M- i: H/ O# _3 X8 i# u+ z$ U
当程序中问题相对稀疏、相对较小时,主要是边际效用递减定律在发挥作用。当程序中问题很多、很严重时,当很多人同时或先后都会遇到时,阻碍开发和检测就会成为主要方面。
& q! f3 u" K, M  ~& r' [# q
) U. g5 q9 P" R& T
昀后是第四个视角,从解决问题的角度看。有的问题容易找出背后的原因,容易修复,有的问题不容易修复。
修复一个问题的代价,除了跟这个问题本身有关系外,还跟在何时修复它有很大关系。在问题产生后越早修复,修复的成本越低。

5 f+ q" f# D, D( K9 Z; r6 y$ Q$ \首先,从过程上讲,程序员在提交前通过检测发现一个问题,他可以立刻修改代码,改正这个问题,无需跟谁申请,跟谁交互。
- X7 ~+ u& n; O# p
4 Q& N# Z* |0 W! k; {3 O% r4 M然而若是集成工程师在狭义的集成时发现这个问题,通常他先分析定位,再决定是剔除出相应提交还是等待其修复。不论什么样的决定,需要有合适的程序员去解决这个问题,程序员回到自己的工作区,修复并提交。这一系列的工作,比程序员在提交前解决,要耗时费力得多。
, c& o2 h) o. W: d. n1 `& {$ c* k2 [. d3 a6 J1 ^
而如果是在第三阶段,也就是测试工程师测试时发现问题,那就要更麻烦了。要填表,也就是在缺陷跟踪工具中填写关于这个缺陷的详细信息。然后缺陷之间要排优先级,确定哪个先修复,哪个后修复。缺陷总量越多,排起来越困难。然后这个缺陷要分配给合适的程序员修复。程序员拿到描述这个缺陷的材料,要仔细分析研究,追根溯源,找到缺陷发生的原因。修复了,再提交,再集成。这么多的步骤,都是人力物力啊。
- o  T; R/ I3 E0 G9 D- N
其次,从时效上讲,越早修复,修复就越快,因为还沉浸在相关代码的语境中,因为记忆还新鲜。时间久了,都忘了,要把相关内容重新载入到大脑中,这耗时费力。而若不是自己修复,是换成别人来修复,恐怕效率就更低了。# V( V0 ~. P9 t1 U3 o4 {# I

- w" }  }$ K, b* z# |
再次,从方法上讲,以同行评审为代表的静态代码检查,大多在第一阶段进行。它发现的问题,是源代码编写错误本身。作为对比,测试程序运行时的行为,发现的问题,是表象。从表象追查到原因,修改源代码后再重新构建,确认表象消失,这些显然要比修复静态代码检查发现的问题,多花更多的时间。类似的,如果问题能在单元测试中暴露出来,那要比在系统级测试中暴露出来要更容易修复。单元测试经常在第一阶段进行,详细的系统测试,则经常是在第三阶段。

/ e2 B7 w6 l# w越是大型软件开发,越重视同行评审等静态代码检查方法,越重视单元测试。因为大型软件开发往往意味着较长的编译构建时间,同时也更难从表象追查到原因。8 c; m: A) i% J$ x9 S
: h/ u5 n. L8 Q5 S1 i
昀后,从查找范围上讲,若在第一阶段修改,程序员可以比较确定,问题出在刚改的几行代码上,因此他可以重点关注改动的那几行,运用差异调试①,容易定位问题。而等到第三阶段,程序员难以通过检查修改变化的代码来找出问题的原因。

7 W. V0 b# R4 }& z1 `  W基于以上四个方面的分析,我们说,问题产生后越早修复,修复的成本越低。越晚修复,修复的成本越高。
) [7 {& h( c: u8 I" ~
注意,第三个视角发现一个问题的代价,和第四个视角中解决一个问题的代价,都不仅包括发现和修复问题所需的资源和成本,还包括对项目时长的影响。有多大的影响,首先跟集成的阶段有关,因为不同阶段的集成活动,其时长变化引起的项目时长的变化是不同的。

# h) W! R' @: N4 Z# l9 \0 G; v在提交前发现和解决问题,只会耽搁依赖于这个提交的活动。
; _# y: j3 U  J& [1 l
2 B6 I- H0 j! g0 D# I! o
在狭义集成时发现和解决(剔除)问题,会耽搁所有依赖于当前待集成的各个提交的活动。更糟糕的情况是,如果狭义集成处理严重问题的速度赶不上程序员们提交严重问题的速度,使得越来越多的提交等待狭义集成,在瓶颈前形成淤积,那么此时会有大量的活动在等待当前狭义集成的结果。当这种情况发生时,会对项目时长有很大的影响,要特别注意避免。
: U# D: Y7 t: U: m: d& Q
而在集成的第三阶段发现和解决问题,对项目时长的影响要分情况讨论。当项目处于功能开发阶段时,集成的第三阶段与程序员开发新功能并行。此时,不论测试人员是否做全面细致的测试,做多少测试,都基本不会耽误程序员开发新功能。因此,此时集成的第三阶段,对项目时长的增加影响很小。
3 Y7 J5 w/ Z+ Q3 x" s
& v6 h8 q* e; w
而当项目进入稳定化阶段,即程序发布前以发现和解决问题为主
7 c9 v! N  @+ c$ F$ O$ F
要工作的阶段后,项目还需多久完成,将在很大程度上取决于研发团队对于当前剩余问题的发现进而解决的速度。如果稳定化阶段明显,项目时长就很受此时集成第三阶段发现和解决问题的工作的影响。
) p5 p) J5 n3 f& l7 o
因此,早发现、早解决,测试团队及早和经常的开展测试工作,程序员尽早修复缺陷,有利于在总体缩短项目时长。
5 D5 {- d0 z1 K8 a5 d8 x8 f$ F" p
# Z6 h+ _: |. J* m) K6 r/ `' O
作为小结,图 3-14示意了发现和修复问题对项目时长的影响。其中Ⅱ’对应发生阻塞时的集成第二阶段,Ⅲ’对应项目稳定化阶段的集成第三阶段。Ⅲ’并不一定介于I和Ⅱ之间,图中只是示意。

4 ^/ J& G* o5 c
1 `4 K& N: B1 S
对项目时长的影响除了跟集成阶段有关外,不同类型的项目,不同的项目阶段,在何时发现和解决问题,其影响也是不同的。在项目时长主要由各活动间依赖关系决定时,项目时长就比较受何时发现和解决问题这个因素的影响。而在项目时长主要由关键资源决定时,就主要看使用什么资源来发现和解决问题,这个资源(比如程序员或者测试人员)是否是关键资源。

7 A  X( S* h$ v9 \本节内容总结如下,如图 3-15所示。
6 W8 G2 `  T! f1 _0 t! b6 G

9 S( s* q' W( c# h/ k7 k1 c+ `" ~
①相关的,在质量管理理论中,有质量成本( quality costs)这一概念。它既包括为保证满意的质量而发生的费用,也包括因为没有达到满意的质量而造成的损失。详见http://en.wikipedia.org/wiki/Quality_costs
4 N; _  m7 w0 D1 O! s# ~  Z  X8 F* y  i
①参见 http://www.martinfowler.com/bliki/DiffDebugging.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

SCMLife推荐上一条 /4 下一条

QQ|小黑屋|手机版|无图版|SCMLife.com ( 京ICP备06056490号-1 )

GMT+8, 2019-9-22 16:51 , Processed in 0.065598 second(s), 8 queries , Gzip On, MemCache On.

Powered by SCMLife X3.4 Licensed

© 2001-2017 JoyShare.

快速回复 返回顶部 返回列表