您的位置:首页 > 其它

Burndown chart

2015-12-29 01:43 225 查看

S型的燃尽图

在一次milestone开发过程中,开发者会持续编辑issue列表,每个issue都有自己的生命周期。燃尽图预期这些issues会被线性的消灭掉,所以从第一天直接到最后一天画个直线表示预期进度变化,然而实际开发会遇到各种困难,所以实际的进度变化曲线往往不是线性变化的,下面这篇文章给出了S型燃尽图:

https://sandofsky.com/blog/the-s-curve.html

三进制

计算机是基于二进制的,有一句经典的台词是:这个世界上有两种人,一种是懂10进制的,另一种是懂10进制的。但是在开发中,我们常常要接触三进制。程序开发中有一个Keep it simple, stupid的所谓KISS原则,就是说我们写程序,在没有必要的情况下,不要增加不必要的抽象层。例如很多初学者在刚接触了设计模式之后,就会在很简单的程序上不必要或者不正确的使用各种模式去写代码,但实际上那种程度的代码只需要把面向过程代码写好,做好函数这个级别的抽象即可。函数简洁但并不就简单,和通常的初学者相反,有经验的程序员更愿意用简洁的代码解决问题,而不会为了用上一堆所谓特性把代码写成花拳绣腿。一个函数需要考虑入口条件,出口条件,是否可重入,是否线程安全,所依赖的状态变化…等等。所以,有时如果你不放弃装饰,你就得放弃本质。

但是,程序从开发的第一天开始就被持续迭代。有一句话说:程序从被写下的第一天开始就开始腐烂的过程,这是说程序会因为需求的不断变化而需要不断的被重构,一开始几行的代码可能会膨胀,再压缩,再膨胀,再压缩。不同地方的代码会出现重复,各种各样的重复。于是,光有KISS是不够的,我们需要另外一个东西:Do Not Repeat Yourself,也就是所谓的DRY原则。怎样才能不自我重复呢?这就是所谓的三进制原则,同样一件事,重复做三次,就可以考虑批处理,计算机是最擅长做批量处理的事情,例如for循环,递归等。批处理的前提是结构的一致性,结构的一致为将他们装到容器里创造了可能。所以,为了批处理,我们常常需要在结构上做抽象。一致性可能是数据上的,也可能是行为上的,根据程序即数据的原理,数据的一致性可以用行为的一致性表示,所以我们可以只对行为的一致性考虑。在面向过程语言里,可能就是在函数这个级别考虑,在面向对象语言里,则更多可以在接口层体现。把共同的结构装到容器里的过程,就是组合的过程。

好了,三进制大概说到这里。

我们需要专职的TA么?

从我一开始进入程序员这个行业开始,我就也会被软件开发中的人员配置问题困扰。一个团队,是否要有专职的项目经理,美工,测试,产品设计,以及并不讨人喜欢的行政,人力资源?从程序员的角度来说,属于技术偏好型,能用技术解决的,绝不愿意通过增加一个人去解决,能用工具解决的,一定批量处理之。先进技术和工具,常常在效率上是数量级的压倒性优势。

回到小题,通过几年的过程,我想角色上很多时候这些角色都有其必要性,但是,一个角色要有多个人还是一个人甚至直接由工具取代,则是可以因团队而不同。怎样取舍和适应?我认为关键在于:是否有效率的解决问题,例如开发和测试,如果开发和测试两个人不能很好的协作,共同有效率解决问题,而一个两者皆做的更有效率,那么明显后者更好,反之则选择前者。

因此,角色分离,但人员可以合一。也就是人剑合一。

持续迭代

前面说燃尽图,软件开发,常常是一个长期过程。不是一锤子买卖,一个基础类库开发加一个上层产品开发就可能两年。但市场常常不等人,因此我们要紧凑的过程和质量,那么就需要持续迭代。持续迭代体现在一个milestone之内的每一天之间,需要每天跟踪昨天,今天,明天的进度,解决的issue,新产生的issue,代码的提交,测试的通过率,发布的版本,用户的反馈等等。持续迭代同时体现在milestone和milestone之间,粒度会更大一些。

一个理解持续迭代的地方是解bug,任何bug,我们都需要首先尝试去重现它,做二分排除定位,做单元测试刷豆子,搜集证据链,一步步掐头去尾缩小范围,最后解决。这也需要持续迭代。

持续迭代不仅在节奏上保持紧凑,而且把过程渐进展开,我们都知道软件开发的敌人是黑盒子,不能预期的时间和进度,所以持续迭代的过程,我们通过燃尽图,issue管理,bug跟踪,反复把过程dump出来,让进度可视化,促进软件开发的正面化学反应和收敛。

while(rest>e){ step(); }

充分必要

软件开发,涉及到很多需要推理的地方,例如每天的解bug,这里需要逻辑,也就需要充分必要。一个论点,它的论据是否是充分必要的,就是体现逻辑的地方。

其他地方,例如做案例分析,我们训练思辨和分析,我想也应当是基于逻辑的。例如做需求分析,也是应该以是否符合逻辑为核心,如果连自己都说服不了,泛泛而谈的形式化需求分析,多半是需要被重新做的。所以,原则应当是,写完自己读一遍,根据是否充分必要去复审,也许需要补充数据,真实的数据很重要,有数据就可以拿数据说话,一个断言在有数据支撑的情况下,更可能有效。但使用数据要避免单样本统计学家式的武断,同时也要不失管中窥豹的洞察力,这大概需要反复锻炼,反复成功或者失败。

工具

程序员是带工具属性的,工程上,每个小事情都可能有相关的工具可以利用,如果没有,我们就造工具。同样的管理源代码,我们有svn,git,当我们赤手空拳,我们合并别人的代码,可能会先临时拷贝自己新改动的代码到某个临时文件夹S,然后把别人的代码覆盖进来,完了再把S和其diff,再修改。用git,这个过程就是git stash,git pull,git stash pop,所以有时候只要想一下一个操作过程当自己没有工具是是怎么做的,可能工具也大概就是这么做的,只是变成了更有效率的命令。工具也是符合逻辑的。

两个团队A和B。A没做事,B不用工具做事,B比A强;A不用工具做事,B用工具做事,期待的是B比A更有效率的解决问题,否则B并不就比A强;如果两队都做事也都有效率使用工具,则可以开始比内容。所以把那些提高效率的工具都用到习以为常,瓶颈不卡在那些上面,我们就进入比拼真正的最终目标上。反复的训练和使用,可以达到这点。

重试

一个任务,耗时估计的靠谱度跟做过这件事的次数有关,一般来说做过两三次额估计就靠谱点。这也是有经验公式可用。如果它是一个普遍原理,我们可以利用它。从概率上来说,一个事情一次的成功率为a,那么,多次之后的成功率是可计算的,手机打字就不展开了。可以验证,重试可以增加成功率。所以,很多事第一次做,可以预估会有失败和错误,所以失败和犯错时冷静的保存数据,证据,记录,探测的这些点,都可以用来在重试时做可能的规避。不过很多人只尝试了一次,这就回到了经典论述:写100个helloworld不如对一个程序迭代100次。当然,我们要考虑到人是复杂的,喜新厌旧是常态,并不能一概而论。因为,每个人都是独立的个体,选择的自由正是这种平均值。重试要注意两次之间是否收敛,如果不收敛,则重试只会导致队列堆积,甚至引起雪崩,此时要检查更深的问题。

基本和到位

我常常做过一件事以后,发现很多事情并不需要各种奇葩的创新点子之类。做着做着就会看到,做好一件事最需要的并不是创新什么的,而是:概念界定之后的基本功。说概念界定,是因为很多人对自己的职业角色定位不清,就会导致做的事情不能抓住基本点。而一旦界定了角色边界,明确了基本点,那么工作就变成如何完美的把那些基本点做到位上面。例如,保持节奏,从不拖延,持续迭代,细节的完备,以及坚持不越界的做不应该自己做(你得让该做这件事的人去做,有原则对彼此都是一种轻松)。把基本功的细节做到位,就是一种职业素养,和专业主义。我想,这是重要的。我最怕角色不清,要么认为你应该什么都做,要么认为你什么都不要做,角色不清,系统的调度会混乱而没有效率。一个人可以做不同的事情,但最好在角色的语义上区分之。

因此,我也觉得很多事,其实能做到这些的人自己也都可以做好,未必得找别人。这和我经历过到处找控件来组装,到掌握组合控件的基本原理后不认为有魔法控件一样,省去了拿来主义。

提前量和惰性求值

当我们预估,我们会没有充裕的时间做完整的过程,我们可以打时间差。我们说对一个系统做性能优化,不外乎在时间和空间上做优化。如果一个系统或者一个过程要到达预期质量所要消耗的最低时间已经不可再压缩,并且预估到时间整体上将呈现下滑趋势,那么就应该在一开始时间还足够充裕的情况下打提前量,通过一开始的紧凑有序的节奏把大部分脚手架都搭建起来,那么越到后期越会从容和有条不紊,反之则会手忙脚乱。凡事预则立,不预则废。

相反的做法是,对事情做惰性求值,直到我们真正需要一个事情的结果时,才会去做这件事,惰性求值有时候能最大限度减少当前要做的事情。但是,惰性求值相当于一次异步投递任务到队列的过程,队列里等待被做的事情会堆积,当需要在限定时间内求某个事情的结果时,队列可能已经雪崩了。所以有队列的地方就要防雪崩,队列不能过长(根据优先级丢弃),保证你的队列可以在最低粒度时间片内被有序求值。如果你不想你必须要做的事情被丢弃,那么那些事情可以通过打时间差提前做掉,达到超出预期的效果。再说异步任务,异步任务都可能由于各种原因而不会有返回值,所以有异步就要有定时器,做超时监听,一旦超时,做对应的处理。

版本号

无论是10进制,还是10进制,亦或是点分10进制。它们都是版本号。我们的人生就是在年轮的版本之间迭代。认真对待每个版本,麻雀虽小,五脏俱全。每个版本开始,明确本次版本的目标,屏蔽其他,只指目标,当其达成,测试通过,打包,发布,写上:

版本 0.0.3

修订了xxx

移除了yyy

改进了zzz

新增了abc

版本和版本之间,贵在持续,节奏均匀。版本的发布日志,就是一个diff。两个集合,A和B的对称差,就是发版的日志。这让我想起,从写程序到现在,在各种场合写过两个数组,区间列表等的diff。很多时候,我们都在做diff,我常常从程序处理的过程得到一些概念,借以类比或者理解生活的事情,然而并不将其泛化,人太容易只看到类比事物之间共性的地方,而其实事物的复杂更在于那些不同的地方。手里有一把锤子,就到处找钉子的事情,永远都有。

拥塞控制

经典的生产者,消费者情景是这样的:

过程中会不断产生任务,加入任务队列,这是生产者

处理器会定时刷帧,每一帧都会检查当前的窗口,在窗口内的任务就会被执行

每个任务执行完都会更新系统的资源状态,这些状态用来更新窗口,超时的任务可能没执行完需要重新投递到队列里。

这样的过程,不断执行的任务,获取或者释放系统资源,而系统资源变化可以反馈到调度器的允许执行窗口,每一帧则从队列里取出窗口内的任务执行。

我们说,这是一个拥塞控制。不做拥塞控制的情况下,在执行的任务可能太少而没有充分利用系统资源,或者在执行的任务太多而把系统资源竞争到卡死,无论怎样,系统的资源利用率最饱和和任务的执行最多,是一个优化目标。所以,一个健壮的系统,有队列的情况下,要做拥塞控制。

拥赛控制的核心法则是:多还少补,在慢启动、拥赛避免、快速恢复3个阶段来回切换。

分段燃尽

从瀑布到敏捷,有的人说我是classical的,严格的瀑布有序经典;有的人说,我是删繁就简的,小步敏捷迭代,蚂蚁搬家。

就像我认为角色需要分开,人员可以合一。无论是学理上还是实践上,过程的不同阶段是存在的,但是具体实施上可能会压缩和合并。

Plan, Build,Test,基本的控制点在于这三者,侧重点各不相同。全局一次,还是反复多次,只是调度策略的不同。

假设过程是一条插值曲线,那么分段插值曲线比全局插值曲线有更好的局部性,学过数值分析的应该都知道全局插值曲线在控制点多了之后,插值多项式的次数高,会有振荡现象,就是由于局部性不好。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: