《Scrum敏捷软件开发》散记

2015 年 6 月 23 日

1、要有欲望和激情;

2、悄悄开始,不要口号和术语,这样可以只从几个方法起步;

3、用户故事取代需求文档;

4、找一个快失败的项目开始;

5、注意:大多数团队都会过高估计他们在第一个Sprint能取得的成绩;

6、scrumMaster必须在没有人事权力的情况下,成为指引自组织团队的领导者;

7、好的scrumMaster不替团队做决定;

8、尽量组件特性团队,而不是组件(为软件服务的软件)团队;

9、第一步:大家一起来写用户故事;

10、传统变革的三个步骤:“破冰,解除现有形势的限制”,“凝固,使新状态可以持久”,“长期的相对稳定状态被短暂的国度打断”;而scrum,完全不寻求稳定态,团队长期在不平衡状态下工作以保持拥抱变革;

11、给产品做一个包装盒、新闻稿、杂志评测文章,给产品一个一句话描述;

13、自组织团队是需要被影响的,如果不理想,不要直接改变它,引入变量促进它的改变;

14、领导者真正的能力不是知道全部答案,他们真正具有的能力是推动团队朝着更敏捷的方向前进;

15、用户故事 —— 作为一个<用户类型>,我想<某个目标>,以便于<一些原因>。

16、“文档用于用户任务交接时,它们是罪恶的;而当它们用于捕捉交谈记录使其不被忘记时,则是有价值的。” —— Tom Poppendieck 《精益软件开发》

17、写文档的时候,问问自己是否愿意一直更新它。或者,给文档一个过期时间。

18、增量与迭代的区别;

19、可交付意味着“测试过”,意味着“集成已经做好”,不意味着“功能完备”;

20、保持协作,所有人知道所有的设计和愿景;一个阻止大家认为这是别人的事的提问——“如果xxx同学放一个长假,我们还能在两周内完成这个功能吗?”。这个问题可以迫使大家不把问题看作“别人的问题”,进而深入思考;

21、没有交接,同步进行。不存在功能从一个人做好交给另一个人,大家互相了解,同步进行;

22、Sprint中应该有10%的时间用来思考和计划下一个Sprint;

23、Sprint是定长的,这就是所谓“节奏”;

24、绝对不要延长Sprint,这就是所谓“纪律”;

25、敏捷不意味着随时变化,在Sprint期间,任务和优先级都是锁定的,即使是有了更好的设计;如果一定要变化,终止当前的Sprint,开启一个新的Sprint;

26、可以加班,但不能连续两周加班;加班是项目出现问题的一个征兆;

27、不要再告诉管理层我们的大脑每天六个小时后就枯竭了。有多少持有这种观点的开发人员在他们完成白天的工作后,晚上回到家后,由于个人兴趣,继续为一些开源的项目做贡献呢?

28、区别对待估算和承诺!

29、对付技术债务的方法:1、止血;2、维持现状;3、还债;

Tags

《晨会逐字稿1》

今天是我们第一次站会,如上周所说,我们从今天起要致力于以两周为周期,交付可使用的软件。

这意味着,在两周后,也就是下下周三,我们就要做出我们第一版可交付的软件。

“可交付”的意思呢,就是要输出一个“产品”。

看过《人月神话》的同学们都知道,产品和代码之的差距,就是文档。

其实不只是把代码写完,还要要配套的文档和部署集成的工具,就像一个开源产品一样。

就是做到可以拿出去卖的程度。

今天是一次,我们就不回答“我昨天做了什么”、“我今天打算做什么”、“有什么阻碍”的三个问题了。

大家先把第一个两周轮回的目标一起讨论一下。

这次讨论呢,我们改变一下我们描述目标的方式,不再用功能列表清单和软件模块的方式,而是使用这样的描述:

(一边在白板上画一边说)“作为一个<(什么什么样的)用户类型>,我想<(达成)某个动作>,以便于<(实现)一些愿景>。”

谁来说一个?

……

没人?那我先说一个。

一个笼统的用户故事是这样的:“作为一个创业开发者,我想在多终端上直播视频流,以便于做一款一边看电影一边讨论的app。”

我们其中一些人是有过类似的用户的经历的,对吧。

比如说瑞恒,被要求过演示手机视频直播;开富老师,也接过同事要求做现场视频直播的要求。

我们一起来使用这样的句子来描述我们的OVS吧。

不必完整,只要是能描述出OVS使用的一个场景就可以。

(一阵讨论之后……)

OK,我们已经有了一些用户故事,我们把它们分分类……大的…小的…(在白板上画backlog冰山图)。

看起来我们不太可能在第一个两周内完成这样大的描述,让我试着把它们分解一下,分解到两周内可以交付的描述。确定第一个轮回我们可以搞定的描述。

(一阵讨论之后……)

我们具体来看一下,我们有没有可能在下下周三前交出这个描述。

(a同学可能来做A事情,b同学可能来完成B事情。一阵讨论之后……)

注意哦,大家可能觉得事情挺简单的。现在我们的要求不一样了哦。我们这次和之前不同,我们在下下周三要提交的是一个产品,意味它除了代码之外,要包含测试。

这个测试呢,不是倒数第二天,有人问一句,有单元测试吗?有。都过了吗?过了。这样的测试。

而是从第一天就开始写,并提交到GitHub的测试。

我们会在每天早上的站会上讨论这些测试,互相提出建议。并在倒数第二天通过这些测试。

还要包含文档。是正经的文档,像我们用过的varnish、x264那样的开源项目文档。以HTML或者README.md的形式。

我们会交叉着互相run其他人的实例,在只有文档不对话的前提下,能run起来是前提。

文档中还要表明这个项目的目的,使用场景,如果可能,还应该有性能介绍。

另外,它必须是集成好的。开箱即用的产品。

Tags

《创造知识的企业》读后小结

2015 年 6 月 19 日

买这本书来读的最主要原因是,这是第一本系统提出scrum的前身“橄榄球开发方法”的书。

原来日本人在这里也捷足先登了。

总体来说,很精彩,但废话挺多的一本书。

不过也许就像作者讲的,“冗余”可以激发创新,一个团队中有会相同技能的多个成员,若能强迫他们密切沟通、互相激发,就有机会带来创新。

书中把若干概念反复强调,也确实促使我对它们反复的思考。

一、为什么要持续创新?

“对以往成功的过度适应是危险的,恐龙是最好的例子。” Page196

二、创新是什么?

“创新的精髓是根据具体理想或愿景来重新穿造一个世界。” Page10

作者的回答很有意思,他说,创新是理想主义。

三、中层管理应该做什么?

作者认为企业创新应当以中层管理为主力。

高层提出类似“让我们一起去冒险”,“人性化电子”等模糊的目标和愿景。目的是给组织注入方向感和理想主义

中层把模糊的理想和愿景转化成中程目标,提出更具体的概念,比如“汽车进化论”,“Easy & Rich”。

他们要做的是——

帮助团队注入冗余,加强沟通,表出“暗默知识”,创造概念,建立原型,共同化“形式知识”。

本质上,不仅仅是为了完成当前的工作任务,更是为团队得到思考和合作的基础与共识,而非“领导分解任务-下属逐项完成”式的。

然后促使团队围绕这个目标不停地自我磨合和优化。

最终还给高层一个更懂自己的目标和行为方式的团队,以在未来创造性地达成各项任务。

或者更进一步地交给高层更适合本公司的创新方法论。

而不仅仅是完成目前这一次的任务。

其实既然说到了创新就是理想,那管理就是带领团队寻追理想的过程。

作者把这种以中层为主力的创新管理方式称为“承上启下”式的管理方式。

四、怎样的组织适合创新?

不是GE那种由上之下的组织形式,也不是3M那种由下至上的组织形式。

作者认为,最理想的是美国海军陆战队式的 ——

战时临时抽调并有绝对的自主权,这样有利于创造知识;

非战时大家又回到金字塔式的组织架构中去,这样有利于沉淀、总结和传播知识。

作者把这种组织称为“超文本”式的组织结构。

他认为Sharp公司是典型的这样的例子。

五、如何建立一个独立创新小队

在团队内引入技能冗余和必要的多样性;

让团队自治;

强调体验“暗默知识”;

同时交给团队清晰的挑战性目标和模糊的组织意图。

清晰的目标:
1、只需将材料放入,其它都不用管;
2、材料不需要特殊配比;
3、制作不受室温影响;
4、面包的外观必须好看;
5、面包的味道要比大批量生产和销售的要好;
6、零售价格在4万日元以下;

模糊的意图:
Easy & Rich (简单却富有营养的)

六、提到“橄榄球”式开发的弊端

Page 138

“它也可导致原有设计和产品规格不断变更的弊端。

橄榄球式开发过程过于依赖共同化模式。

随着因项目所涉及人员数目的增加以及要求变动的数目加倍,

这无异就导致了工作效率的降低。”

七、段子一则:“3M公司没有上司”

Page164

20世纪50年代末,《华尔街日报》采访3M总裁布埃托。

采访过程中,记者要求看看公司的组织图,但布埃托总是推辞。

最后记者无奈之下问道:“贵公司是不是没有组织图?”

布埃托一边局促不安地在抽屉里寻找,一边回答到:“哦,我们的确有一个,但我们不喜欢把它摆出来,如果这里的一些人知道自己的上司是谁的话,他们会感到很不舒服。”

《各位,我们进入敏捷过程吧》逐字稿

大家都能感觉得到,最近一段时间我们的开发效率不理想。

产品总在呼之欲出的状态,就是出不来。

我一直为我们这个Team骄傲,一直说各位都是单兵能力高于平均水准的专家,这是事实。

可比较遗憾的是,最近我们作为一个团队,没有表现出和我们单兵能力相匹配的结果。

这次我休假之前,分别和大家谈的时候,曾经许诺给大家,回来后我们要改进开发流程,让开发工作的节奏回到正常的节奏上来。

我们也确实需要摸索出一个更适合我们的协作模式,这件事需要我们大家一起来努力。

我也一直说,我们致力于不加班的工作。可是“不加班”肯定不是最终目标,那样不上班就好啦。不加班的真实含义是在工作时间里高效地工作。

首先,我们要做出这样两个改变:

第一、每天早上10点,我们一起在休息区开个10分钟的站会。

站着开会主要的目的是缩短会议时间。

会上每个人回答3个问题:昨天你完成了那些工作?今天你打算做什么?完成你的目标存在什么障碍?

第二、以两周为一个轮回,我们要制定工作目标,然后冲刺。

每个轮回的结束,都要产出可能缺少特性,但实际可用的产品。

我们首先将以miniSUE和OVSv2的最终完成为目标,下周开始我们一起来做具体的任务分解工作。

现在,作为开始,大家各自回答一下3个问题。

昨天你完成了那些工作?今天你打算做什么?完成你的目标存在什么障碍?

从元元开始吧。

strace追踪多线程程序

2015 年 6 月 15 日

方法一

先用ps -mp pid或者top -H查出线程pid。

然后strace -p pid追踪其中一个线程。

方法二

直接用strace -fp pid追踪进程下所有线程。

Tags

[rust]i64转i32

2015 年 6 月 13 日

Tags

[rust]三个方法解决error: use of moved value

2015 年 6 月 4 日

概述

“error: use of moved value”,相信最近开始玩rust的同学看到这个报错都能会心一笑了。

rust做到了不依赖运行期垃圾回收的安全内存管理,但这个特别爽快的特性也引入了一些不便,这报错就是常见的麻烦之一。

这报错要是想展开说清楚,需要完整解释rust的ownership、borrowing、lifetime等概念,那是一篇太长的文章。

我们先暂时放下概念,用三个不同的方法动手解决这个报错。

错误

我们以下面这段程序为基础展开我们的讨论,这里面主要定义的就是一个Info结构体。

首先,我们要制造出报错“use of moved value”。
很简单,我们只需要以foo为参数再调用一次fn_a()就好。

现在,我们得到了编译器报错。

编译器说,我们新加入的行里用了“移动了的值”。
啥叫“移动了的值”呢?
说白了就是用过了的值,foo已经给第一个fn_a()用过了,到了第二个fn_a()的时候就是moved value了。然后就不让用了。
至于为什么要制定这样的规则,我另撰文解释。

现在我们开始动手来解决这个问题。

方法一:引用

因为我们传给函数的是value,所以value被move了。
我们可以通过传引用给函数来解决这问题。
代码如下:

运行结果如下:

我们把这段程序用impl改写一下。
下面这段程序和上面的程序其实是等价的:

运行结果显然是一样的:

改写成这样之后是不是很眼熟了呢。

没错,大多数的标准库就是用的这种方法来避免moved value问题的。

impl里面的第一个参数其实就是struct的引用,所以我们用struct.fn()这种写法的时候,传递给方法实际都是引用。

方法二:引用计数

rust在标准库里提供了引用计数,它是另一个可以解决move value的方法。
先列出来代码吧。

这段代码有点稍稍复杂。
其实需要注意的地方只有两个:
1、使用了Rc之后,传递变量都要使用.clone()方法来增加引用计数;减少引用计数不用管,rust会根据作用域自己搞定;
2、变量不用声明为mut了。使用的时候,如果不更改,使用.borrow()方法得到真正的struct;如果需要更改,则使用.borrow_mut()方法得到真正的struct。

另外还有两点需要再说明一下:
1、如果变量根本不需要改变,则不用套里面的RefCell::new()那层;
2、如果涉及多线程之间的传参,要放弃Rc,使用Arc。

方法三:实现Trait Clone

从方法二中可以看到,我们使用了Rc,Rc就帮助我们实现了一个.clone()方法,让我们得以避免错误。

那我们能不能自己实现.clone()呢,答案当然是肯定的。

Rc做的是把数据和计数都放到了堆上,它提供的.clone()实际是对计数的复制。

我们不搞那么复杂,我们做个简单点的,我们直接做对struct的复制。

代码如下:

运行结果:

为什么我们这里没有写foo.clone()而直接写foo编译器也没抱怨什么呢?

我们可以看看最初编译器说的话:note: foo moved here because it has type Info, which is non-copyable

它说,foo因为不能复制,所以它被move了。

所以当我们让Info变得可复制了之后,它就不会被move了。

当然要写成foo.clone(),那也是没问题的。

但是注意因为我们的值是复制进去的,所以最原始的foo不会被改变了。

最后

以上我们用了三种方法解决了move value的报错。

实际还有很多种方法可以用于在不同的场景中避免这个错误。

有兴趣的同学可以继续玩耍,最后提供一个跟踪变量生命周期的方法以供愉快玩耍。

实现Drop Trait在变量被释放或重分配空间时打印日志,代码如下:

[rust]使struct可被打印

2015 年 6 月 3 日

这块变化挺大的。
在1.0里的做法是实现Struct的Trait——std::fmt::Display,代码如下:

一般来说打印struct这就够了。
但是如果这个struct还会被包含在别的struct里面的话,可能需要用到Debug打印,也就是{:?}。
代码如下:

九个例程认识Rust中的引用

2015 年 5 月 26 日

为方便理解,先简述一下Rust中的所有权概念:

一个变量同一时刻只能有一个拥有者。

完整的概念参看Rust Book: https://doc.rust-lang.org/stable/book/ownership.html。

例一

通过引用把变量借给方法,方法结束后,会自动把变量的拥有权归还给主逻辑。

例二

例一之堆版本。

例三

引用没有归还之前,原变量不可使用。否则报错。

例四

例三正确版。
给c一个作用域,超出作用域时c会自动归还b。

例五

例四基础上另一个多次借出的错误示范。

例六

函数是可以返回引用的。

例七

但是返回的引用必须是传入的结构体里的东西。这样可以保证只要传入的变量活着,返回的变量也会一直有效。
因为如果在函数内部声明一个变量,返回它的引用的话,出了这个函数那个变量也被回收了。
像下面这样是不行的。

例八

但是如果传入的变量有两个,编译器就不知道把返回的指针的生命周期和其中哪个绑定了。
于是报错,等我们解决。

例九

例八正确版。
使用“命名生命周期”解决例八的问题,告诉编译器绑定第一个变量。
这里第一次出现了“’a”这个东西,一下就出现了三次。
它就是“命名生命周期”。

出现的这三次可以这样理解:
第一次:声明’a,我这个函数接下来会用到;
第二次:给’a赋值,’a等于第一个参数的生命周期;
第三次:把’a的值赋给返回,返回的引用的声明周期等于’a。
这样就把返回的引用生命周期和第一个传入的参数绑在一起了。

‘a也可以是’b,’c,’d,a-z都可以用的。

改成下面这样就可以运行了。

请这样理解’a:它和var一样也是函数的一个参数,同样是由调用者赋值的。
在这里例子里面,在函数被调用的那一刻(标记A)’a就被foo赋值了。

Tags

DASH标准阅读笔记(一)

2015 年 5 月 6 日

功能上和HLS相比的强处

1、支持inband事件流;
2、切片可以用模版描述,避免轮询索引;

功能上和HLS相比的弱处

1、没有冗余线路的支持;

3个无感切流的实践

1、关键帧;
2、时间上不相互重叠的切片;
3、切片拼装起来后和原始流数据一致(注意时间戳);

关于MPD

1、MPD是XML;
2、MPD支持GZIP压缩;
3、加密和完整性内容保护都不是DASH标准的一部分,但是可以通过XML的W3C标准中相关内容来实现;

数据分层概览

1、Period -> Adaptation Sets -> Representations -> Sub-Representations-> Segments -> Sub-Segments;
2、Adaptation Set、Representation、Segment都可以指定码率、sar等视频相关的属性,按照级别一级一级覆盖,低级优先;

Period

是数据分层中最高的一层,内含一段时间的内容;

先发Period

先发Period是Period的一种,用于表示流的一些信息,类似头信息。
同时满足以下条件的Period就是“先发Period”。
1、直播;
2、不是MPD里的第一片;
3、不包含@start属性;
4、前一片没有@Duration属性;

Adaptation Set

1、是Period的下一级;
2、是Representation的上一级;
3、包含多个可以无感切换的Representation;
4、可以声明下面所有Repersentation的码率总范围;
5、可以声明关键帧是否是对齐的;

Media Content Compontent

1、是Adaptation Set的下一级;
2、如果一个Adaptation Set中只包含一个Media Content Compontent,可以不单独出现这个元素,直接在Adaptation Set的属性中声明;
3、可以指定par、lang、contentType、role、viewpoint等属性;

Representation

1、是Adaptation Set的下一级;
2、是Segment的上一级;
3、时长与Period相同;
4、可以是自启动的,也可以通过指定@dependencyId属性靠依赖启动;
5、直播时的时钟补偿操作在这一层做;
6、Segmentlist属性只能指定一个;
7、要声明自己的@qualituRanking;

Sub-Representations

1、用于音频、字幕、低码率视频;
2、提供同内容的低质量流,比如说只包含声音或者文字的;
3、是包含在Repersentation之中的,而不是与之平级;
4、如果指定了@level,就必须指定@bandwidth;

Subsets

1、是Period的下一级;
2、负责聚集多个Adaptation Set;
3、不可以用一个Subsets聚集所有的Adaptation Sets;
4、主要作用就是聚集Adaptation Sets;

Segments

1、可以指定这个片的“可见时间”,也就是不会404的时间;
2、三种指定Segment Url的方法:
a> 1个SegmentList;
b> 1个SegmentTemplate;
c> 1个SegmentBase加多个BaseUrl;
3、1个representation只包含1个Segment时才用到SegmentBase;
4、1个representation包含多个Segment时用SegmentList或者SegmentTemplate;
5、多个Segment时必须指定@Duration属性或者SegmentTimeline元素其中之一,但不能都指定;
6、SegmentURL支持指定HTTP Range;
7、可以通过SegmentList.href关联远端SegmentList;
8、SegmentTemplate模版使用示例: %05d。printf的格式都支持,还可以关联representation中的@Id和@bandwidth等属性;
9、多个Segment时,以下4项可以帮助定位:
a> URL+HTTP Range;
b> representation中的序号;
c> 开始时间;
d> 时长;
10、模版中$time变量是指之前所有片的@Duration之和;
11、SegmentUrl尝试解析顺序如下:
a> SegmentTemplate + Number;
b> SegmentTemplate + Time;
c> SegmentList;
d> SingleSegment;
12、Segment的“可见时间”可以等于:
a> representation的结束时间;
b> MPD的“可见时间” —— Period.@miniumUpdate属性;

Tags