仓库源文

.. Kenneth Lee 版权所有 2021

:Authors: Kenneth Lee :Version: 1.0 :Date: 2021-12-25 :Status: Draft

关于敏捷项目的一些逻辑


最近大项目在一个子项目上遇到一些障碍,我下去直接带了一下项目执行。我很久没有去 具体带项目了,看到不少人执行敏捷项目上的问题,我把这些杂七杂八的问题通过本文记 录一下。

一些组织实施敏捷项目,就实施了个样子,站会,代码代替文档,Story驱动,测试驱动…… 好像做了这些形式,就是在做敏捷了。

我对此不以为然。敏捷的驱动力其实就一个:软件变大以后信息不足成为主要问题。

八十年代的时候,我们做一个软件,除了最核心的需求,全部是自己的判断空间。所以我 们可以分层设计,建立第一层逻辑,基于第一层逻辑设计第二层逻辑,只要高层设计没有 明显的问题,下面遇到的障碍是有限的,要打印就要自己写一个printf的库,要排序就自 己写排序算法,要用链表就创建链表。就算有一些开发库,也是用相对标准的,Posix, STL等等,这可以支撑你做瀑布模型的设计,下层的设计可以完全根据你的需要来定制。

但现在不行了,你下面一堆的库和框架,你用Log4J吗?你用glib-thread吗?你用 tensorflow-2.0吗?你写Linux驱动用哪个版本的内核API?这些不是你高层设计说要怎么 样就怎么样的。你要不用这些框架,要不自己开发,但你能自己开发吗?老软件几十万行 代码就是大型工程了,现在随便弄一个什么解决方案,代码量都超过千万行,甚至上亿行 ,而且还只给几个月的开发时间。你怎么自己开发?

而且,就算你很厉害,对下面用的的库的特点通盘掌握(我只是比喻,现实其实这不可能 ),问题是这些库本身也在升级,每天都在变,你怎么通盘掌握?

为了这个通盘掌握,你就要通过写代码去校验这些变化中的底层,但这个底层逻辑隔几个 月就会变,你的用户对你做的功能的要求,也隔几个月就会变,你运筹帷幄提前想好一年 内怎么开发,毫无意义,因为提前做这个判断基本上都是错的。这种判断就好像判断明年 今天会不会下雨一样,没有判断的条件。

所以,敏捷不是不要设计,也不是站立会议,而是要动脑子。每走几步,就要重新Review 一下自己的状态,然后决定怎么改向。

所以,管敏捷项目,我们其实最重要的要求是项目组成员都要动脑,我们只能给你条件, 不能告诉你路径,不能告诉你这一步怎么走,那一步怎么走。

所以我一般不开什么站立会议,我是在项目的代码库中直接放了一个devlog的目录,每个 开发成员每天更新进展。这个进展也不是“我今天做了什么”,而是一个技术判断过程:我 今天处于项目的什么位置,所以我需要做XX,然后我技术上有如下判断:……。这种判断可 能包括一个逻辑闭包的描述,说明怎么设计的。也可能是调试一个程序的coredump记录。

这种日志有两个作用:

  1. 随时随地做设计
  2. 事后可以找回开发过程中的一些信息

工作日志当天离开前提交,然后我作为项目负责人的工作是每天一早回来Review一次所有 人的工作,看看项目有没有走偏,引导走偏的人回来,遇到有影响所有的人问题拉会议出 来讨论。其他人做当天的准备的时候,也一样去看看其他人的进展,据此做他的计划。这 样构成一个循环。

所以,敏捷的关键在于“敏捷”,是脑子不断在动的。不是死气层层围在一起,每个人给项 目经理汇报工作:“我昨天做了XXXX”,项目经理抽一下鞭子:“不错,好好干”,或者“这样 慢可不行啊小李,别人都在等着你呢”。这他么敏捷啥呀,和过去的项目有区别吗?

所以,敏捷也不是只写代码不写设计。它不过是无时无刻都在写设计而已。而且我们也会写 复杂的设计的,比如我们最近几个目标的技术风险看着没有了,技术方向确定了,我就开始 找人写测试计划。写测试计划这件事,就不能靠编码搞定,显然它就是一个设计了。

有人写这种计划就真的是个计划,几号几号,完成某某测试,几号几号,有完成另外的什 么测试。这种也是充样子:你连打算怎么测都没有定呢,你怎么知道你几号几号的就能完 成这个测试?——装样子。

我不是说不需要时间点上的计划,这是两种不同的建模模型。如果我们有一个交付点,在 那个点上如果不完成测试,就会导致上市的准备工作匹配不上,我们为此建模,确定我们 日常的资源和技术安排的时候为那个时间点做权衡,这就可以定这样的计划。

但我们前面明显说的是技术障碍消除了,所以为了能后面一下就可以测试,而做一个怎么 测试的“计划”,这要的是技术方法,而不是时间点啊。

所以,我这里强调的我们做敏捷,首先是实事求是,而不是传统那种大公司病的“你说计划 ,就是时间计划,我没有写好,是你定义得不够详细……”这种应付任务式的工作方法。

不写这种时间计划,有人会走另一个极端,那直接写一个个的测试用例咯,但他又写不出 来,因为开发还没有完成,部分细节接口还没有确定,然后他就乱“理解”了一个接口,硬 写了一个测试代码出来……你看,这样同样不实事求是,而是在表演,这同样不敏捷。

我们觉得测试这个事情可以提前做,是因为我们确实有提前做的基础。比如我们做一个MCU, 使用自己的指令,具体这些指令的细节我们还没有最终定,因为在开发的过程中,我们可能 根据业务流的不断优化,修改它们的详细接口。但我们已经知道我们的MCU的主要功能, 大概有什么指令,编译器和OS是怎么样的。那我们就可以基于这种信息做个打算,我们可 以确定我们的测试目标(我们的测试主要测试什么问题)和测试策略(我们主要用什么方 式组织这个测试,为什么这个测试可以发现我们的系统的问题),甚至我们确实也可以写出 部分的测试用例,比如我们不关心我们的指令怎么写,我们可以提前构思:我们一个用例 是把n加100次,然后比较它和n乘100的结果,看两者是否对得上,我们故意填充一段代码, 测试跳转到跳转距离的极限,以及比极限小一,以及距离0的位置,以这些极端的情况看跳 转行为是否合理……这些都不依赖深入的接口定义,但我们确实可以提前进行准备。这些东 西,不用代码表达,但它确实简化了我们的编码啊,而且从这个维度建模,是代码不可替 代的,因为写每个具体测试用例的时候,我们不会盯着我们整个指令的列表,看看少测试 了什么东西,看看可以如何组合这些指令进行综合测试。

所以说,敏捷不是不写设计,而是把代码也看作是一个设计视图,其他建模也是设计视图 ,而且因为所有的视图都在变,我们尽量都放在一起管理而已。在我们的项目中,无论是 开发日志,还是设计,全部都用Sphinx来写,这样就更加看起来,全都是代码了。

但问题的关键不是这些形式,而是不断变化,不断动脑的这么一个“敏捷”地走向目标的过 程啊。

最后说一个事情,我给前面提到这位做测试计划的兄弟谈“目标和策略”的方法时,他问了 一个问题:目标和策略有什么不同?这个问题问得我一愣。

直接解释,目标是内涵,策略是总结性的外延。目标是:“我想看到软件运行我们的MCU没 有错误”,但怎样才能达成这个目的呢?你又不能把所有软件都运行一遍。

解决这个问题的方法是“外延”,我们用这个,这个,这个,还有这个方法折腾一次我们的 设计,我们一定程度上就达成这个目标了。

而策略是对这些方法的抽象,提出一些最基本的,在我们展开成一个个个例前的高层判断 ,避免遗漏,走偏和浪费而定义的范围。

这个很简单,但为什么他会有这个误会呢?我想这同样是不习惯敏捷项目的特征引起的。 敏捷项目中,所有人存在的价值是“动脑”,是补全逻辑,所以,目标和策略是有距离的。 是要输入每个开发者自己贡献的逻辑的,如果你习惯了别人把所有事情怎么干的细节都告 诉你,就容易会误会:目标和策略,是同一个东西。

补充

下面是一些在本文写完后,在项目运作过程中发现的一些独立问题,我也记录一下。

汇报还是领路

我把计划跟踪工作转给了另一个项目成员,他第一次写计划跟踪,是这样写的:

  1. 目前提供了一个可协作开发的初始化版本
  2. 特性分配计划已经完成,后续进行分工设计工作
  3. ...

我不知道读者是否注意到了,这个计划跟踪是写给我看的(或者说写给他的领导看的):

“小李,今天计划完成得怎么样了?”

“目前提供了一个可协作开发的初始化版本。”

“不错不错,看了个好头,继续努力。”

这是汇报工作,而不是给团队领路啊。给团队领路是怎么样的呢?

  1. 写出开发初始版本已经完成,在master分支上,打了一个tag: init_frame,请所有人 今天完成review和反馈意见,没有问题,我们明天用这个作为基础来工作。

  2. 特性分配计划分配完成,参见下表,请所有成员按表中要求实现功能,合入dev分支……

这才是给团队看的东西,不是给领导做样子的工作啊。

可以运行的代码胜过文档

这句话的本意是,如果代码可以运行,我可以忽略很多细节,用一个直接跑起来的代码开 始修改,来确定我的一个“假设”是否成立,这比理解了很多代码逻辑,然后脑子里构成一 个结论要靠谱得多。

但这句话也说明了,人脑的逻辑判断能力和包含所有细节的事实是不能比的。

比如我们定义了,本周完成xxx特性的全部代码合入。第二周有人给我回复说,“这个每个 人都完成了,也联调过,其实也算完成了。”,这仍是糊弄领导的口气,谁在乎“算不算”啊 ?算又如何,不算又如何?可能拿去卖钱吗?我们说本周要完成xxx特性的合入,就是在代 码树上看到这些代码(当然我们对特定分支合入是已经有要求的),没有进去,就是没有 进去。你联调过了,你讨论过了,只要没有进去,就必然是存在某个细节,导致你没有进 去,脑补出来的差不多,可能差很远,所以管项目我们能不靠这种脑补解决问题就不靠这 种脑补解决问题,尽快看到可以运行的代码,才能减轻我们的判断工作量。

敏捷合作

有些人在项目中改代码的时候,总是不管其他人修改了什么, 即使是同一个模块,甚至同 一个文件,别人更新的逻辑,修改了变量,加入了新的公共函数,他都按自己原来的设想 该怎么写程序就怎么写程序,这样的结果是代码里面的逻辑全是分离的,这样也是一种不 动脑,结果程序里面逻辑全部互相独立,逻辑冗余或者很容易冲突。

不动脑的合作,代码树没法开放让所有人都可以push,否则很容易就破坏主线了。