度量 | 敏捷的哲学

我们喜欢度量,只是因为我们害怕不确定性罢了。

拟定的目标,但未成为现实之前,是摸不着的空中楼阁。我们如何确保自己以及众人,做出了正确的努力。度量,是在到达那一天之前,本能之下会想到的办法。遥望远方,时不时回头看下身后的脚印,再一步一步踱往前去。

只是到了一些特定的局面之中,因为我们内在的人性惯性,企业文化的重力,以及微妙的的官僚和政治,原本有着良好意愿的度量初心,就会很容易被极度简单物化成了只需遵从并实现的指标,并挤压在复杂性下做全局优先思考的空间。

于是,度量势必走向变形的另外一面。我们看到了多少以KPI和OKR之名,行下达摊派之实。极度简化的单词缩写,也简化了人们的头脑。结果只能是懒惰的思考,以及唯一放在产出的关注点。目标平庸还好,无伤大雅。而华丽的愿景之下错误的方向,则会引领人们南辕北辙,日暮途穷。在这错误的旅途上,唯指标和上峰至上,各自为阵,彼此倾轧,只见树木不见森林。

而这又会进一步扭曲我们对于当下存在状态的思考角度。认为这就是现实,无力扭转。

我一度也以为,敏捷更关乎开放的心态,无止境的快速反馈和学习,提起度量的态度是暧昧的。即便见证过敏捷起来的收益,但对于心存狐疑的人终究没有说服力。

《加速》里面提到了四个关键指标:

  • 前置时间
  • 部署频率
  • 平均恢复时间
  • 变更失败率

与误入歧途的KPI不一样,这里的关注点则被放在了亟待建设的能力上。

在我看来,它们是尽了最大的可能,给出让人信服但又难以企及的答案。对于我们的理想境地,用它们去描述,虽然平白,但激动人心。但它们终究是经过大量的案例和数据统计得来的,一个完美主义者的美丽新世界。深刻去理解它们,并能理解在它们之下,究竟隐藏着多少持续改进的意愿和努力,才是最难能可贵的。

重要的是,它们确实为我们提供了可以观察的立足点,看到我们进步,以及我们驶往的方向。

(谢谢王妮的建议)

Share
 

持续改进 | 敏捷的哲学

重要的是持续优化的过程,最终交付只是作为副产品之一产生,顺水推舟而已。

记得在某个场合,跟仝键有这样大致的对话。

面前一段旅程,从起点出发,往着目标进发。这是每个人都会有的功课。对于组织而言,要有自己的目标,商业盈利还是社会公益,也都无可厚非。但我们多少已经习惯,目标的实现,即等同于定义的成功。全神贯注于目标,并致力于实现它,成为普世意义上唯一衡量是否努力的标准。

但在软件行业,是否存在另外一个可以思考的空间。

软件构建相比较其他行业而言,最大的一个区别莫过于,它是知识消费和创造的过程。从业务诉求和价值定义,所有与此相关的信息在人们的头脑和言语间传递和转换。令人失望的是,除去那些自欺欺人的所谓满意以外,我们并没有足够的经验和能力,能表明我们在这些信息承转之间,获得到了足够的心安理得。需求模棱两可,在过程中反复无常,曾经自以为的目标交付物也可以沦落到谬以千里,触目惊心。

瀑布方法已经被证明一板一眼无法再有面对不确定性的从容。敏捷以适应性和响应性,挺身而出。敏捷之中诸多的原则和实践,是显而易见的改进手段,唾手可得,耍将起来也煞有介事。短时间取得显而易见的效果,本就是理所当然。它们一度填充了人们在恐惧面对不确定性洪流时的安全皮筏。但人们也就此误以为,这就是可以赖以顺流直下的救命稻草。

然后就此认为改进完成,已然敏捷。

如果仅仅是就此原地踏步,也算万事大吉。但无奈怎样都会是事与愿违。敏捷实践的整体性诉求,以及企业组织和文化的惯性,总是很容易让团队的微弱努力顷刻烟消云散。不安全感再度笼罩。

迎难而上的改进,是向不确定性讨要回掌控感的本能。但一如字面意义,持续改进,难的不是改进,而是在持续。

持续二字,既是对改进这件事一如既往的执着态度,也是对不确定性和不安全感出自内心的认可。适时后退一步,并不意味着低头,在后退间反而得到片刻静默,以及观察的机遇。

历经辛苦,找到改进的良药,实属不易。解决了一个技术难题,或者澄清了纠结已久的需求,都是件值得庆贺的事情。当下的事情,都可以某种更明快的节奏往前推进。怎么都被看做一件值得庆贺的事情。但起点(需求)和终点(目标)对于确定性的双重挤压,仍然会将我们置回于似曾相识的失落境地。不再奏效的不只是曾经成功的经验,还有尚未消失殆尽的成就感。

这时候,我们可曾有过妄议,哪怕迈出大胆的一小步,质疑我们开头来的路,以及我们要去的方向。

Share
 

形形色色的CI Monitor

个人的观点,持续集成是敏捷软件开发中最为重要的实践,没有之一。而加速反馈的重要方式,就是持续集成结果的可视化呈现,也即信息辐射器,作为团队监控软件构建状态的接口。专业的持续集成平台,不管是像ThoughtWorks的商业产品Go,还是CI开源世界代言人Jenkins,都提供了开箱即用的CI Monitor。

下面是Go的CI Monitor界面:

tw_go

… 

Share
 

物理墙和虚拟墙之争

在敏捷开发中,故事墙作为information radiator,在支撑整个软件团队的日常开发,可视化开发进度和开发中出现的问题,起到非常重要的作用。团队的开发状态和进度,在任何时刻,你只需要抬一抬头,就可以一目了然:当前谁在开发什么任务,是否有闲置的资源,开发和测试的工作分配是否合理,是否有过量的Bug等待修复,不一而足。

虚拟的电子故事墙也应运而生,比如Mingle和TFS,还有一些开源的产品经过改造后,也可以极度模拟实际的物理墙界面,并更加容易创建和编辑并删除。尤其在保存开发过程历史方面,比起物理墙有着得天独厚的好处。物理墙基本上只关注当前迭代的进程,在每次进入新的迭代,物理墙更新,上一迭代的故事卡片被回收或者遗弃,难以定位和回顾。虚拟墙因为信息化的本质,更容易搜索和存档,保留快照。此外,电子故事墙在统计和制作图表方面的功能,更是物理墙无法企及的。电子墙可以随着故事卡的挪动,动态算出当前状态下的各种数据,并渲染对应的实时更新的图表,为团队和客户提供数据和可视化方面的决策支持。更重要的,虚拟墙天生支持分布式开发,因为地区甚至时区有差异的团队,如果需要共用一个故事墙时,唯一的选择只能是虚拟墙,每一方团队都能看到时刻最新的故事墙状态。

如何解决好两者之间的关系,在ThoughtWorks内部以及客户的现场,我们都遇到了类似的问题。一般来说,团队自身或者开发者们更喜欢物理墙多一点,感受直观,把注意力和时间都聚焦在故事卡对应的任务本身,而不是被动地吸引在虚拟墙软件自身以及如何学习使用并挪动虚拟墙上的电子卡上(你会看见在站会时,每一位开发人员在挪动电子卡时会有多么奇怪而有趣的事情发生),对他们是更重要的事情。而对于团队的PM或者stakeholder们来说,虚拟墙可以远程打开,开发状态一目了然,更不用提统计和图表所提供的支持。

很多团队会选择两者都采用,但不得不问到以谁为主以及如何同步的问题。以谁为主还是比较好解决,物理墙面向开发团队,虚拟墙面向管理者和stakeholder们。所以最大的问题在于如何保持两者之间的数据同步。说白了就是,物理墙上的故事卡挪动,要及时在虚拟墙上反映出来,尤其虚拟墙软件对于故事卡的每一步挪动都会记录相应的数据变化,从而为精细的图表和统计提供数据,这样物理墙和虚拟墙之间的故事卡数据同步显得尤为重要。但现实情况和经验表明,数据同步做得很不如人意,开发者的关注点在物理墙,往往容易忘记及时更新虚拟墙上的故事卡,他们甚至对于这样一件重复的事情感到愤怒。而如果由PM每天下班前对两个故事墙做同步,又显得无聊之极。

怎么办?我更相信鱼和熊掌不可兼得,对于一个团队,如果既要享受物理墙带来的可视化和信息辐射的好处,又需要虚拟墙带来的强大的编辑、统计以及图表功能,就不得不去承担因为同步二者的故事卡数据所带来的辛苦甚至是痛苦。选择团队的开发人员来各自负责同步两个墙之间的数据,还是由PM每天找一个正确的时间来同步,都是由团队自己来决定,至少我们是这么做的。你还有其他的办法吗?

Share
 

为什么TDD?

1. 反映真实需求

这里存在先写测试和后写测试的区别。

先说后写测试。根据很多经验,在直接写产品实现代码时,需要考虑需求,同时需要兼顾实现的细节,用什么算法和语法。在对需求和考虑和实现细节间来回,很容易让人产生对其中一方的疏忽,遗漏掉一些需求方面,甚至在实现上存在缺陷。

有人会说我可以通过后写测试来保证。第一经验是,很多人都不会在实现完成后,补充测试,因为还有更多的工作和需求需要实现。第二是开发人员很容易在后补的测试中,只是试图去测试他已有的实现,而不是需求本身,很容易遗漏掉一些边界检查之类,在测试时,已有的实现细节会在脑子里面先入为主,即使实现存在问题或有漏洞,也很难在后补的测试中测出来。我阅读过很多面试者的测试代码,很明显都是后补的,因为一些很明显的问题没有测出来,甚至已经实现的逻辑也是只测试了一部分。而这些面试者都承认。

结果就是,一旦代码签入,开始集成之后,暴露出问题,后补的测试起不到对于实现代码的保障需求的作用,开发者不能不借助调试工具,单步跟踪实现代码的每一行去寻找问题发生的原因。

再说先写测试。按照TDD的流程,先写失败的测试,再写恰好让测试成功的实现代码,最后重构,如此往复。这样的好处在于,每先写一个测试,都是在试图用自己对问题和需求的理解,来定义实现代码的架子。从测试的不同角度,范围、边界、大小、功能等等,来定义实现代码将来会是个什么样子。一个测试接着一个测试,利用TDD的过程,把实现代码恰好不多不少,正好驱动出解决这个需求和问题的实现代码来。

这里的重点在于,先写测试可以让开发者把重点放在理解需求和实现需求上,而不是一开始就陷入实现的细节中同时兼顾需求,掉入两者都兼顾不好的境地。先写的测试代码,作为副产品,可以作为验证需求的得力保障。

2. 设计在其中

先写测试对于设计的好处在于,先写测试先定义新的类,以及定义类与类之间的关系,就是在定义类与类之间如何交互,每个类如何暴露自己的接口,类和类之间的引用关系。这时,测试代码会逼迫开发者认真考虑如何分解类与类之间的耦合关系,这样产生的实现代码更容易利用了IoC和DIP的模式,实现面向接口编程。

这样实现代码的好处还在于,代码的可测试性很高,在加入更多的测试代码和新类的时候,同样借力于已有类的面向接口和依赖反转所带来的可测试性,达到新实现代码的面向接口和可测试性,这样进入良性循环。而这对于整体的代码和设计,获益良多。换句话说,测试即设计。

回头看后写测试的情况,因为从一开始开发者把重心放在实现的细节和功能需求的往复上,对于代码设计、类的关系和定义很容易疏于考虑,产生的结果可能是耦合紧,可测试性差。

3. 增强信心

在我看来,软件开发周期、软件交付最大的问题在于交付后的运行和维护阶段,正是这个阶段才是软件在持续交付价值的时期。在软件的可维护性,在这个阶段凸显价值。在软件交付后,包括软件开发周期期间,不可避免的就是开发者在根据新的需求,逐渐添加新的功能代码,或者修复一些已知的缺陷。

很多经验表明,在开发者按照需求添加一些新的代码进入系统,或者试图修复已有缺陷时,很容易导致既有功能出错,也就是新引入的代码打破了既有代码的逻辑,导致回归问题的出现。因为软件系统的可测试性差,无法做到快速频繁自动的回归测试,带来的可维护性自然也很差。

而作为TDD的副产品之一——可以快速频繁自动运行的测试代码,可以在开发者新引入代码之际,给予开发者足够的信心,每次添加一点新代码,一个方法,一个类,都已频繁运行已有相关的测试代码,来确保新引入代码不会打破已有的功能。

在持续集成中,这些测试代码可以帮助验证每次签入的代码都不会破坏掉已有的功能。这也是《重构》里面反复提到的在每个重构小步骤后都要运行所有的测试代码的原因所在。

4. 粒度和进度

按照TDD的原则,先写测试,可以让开发者在同一时间只关注在功能需求的一小部分,把功能需求且分到一定小的粒度,用测试代码去表示这样的需求,用实现代码让测试通过,实现这样的需求,然后重构。

这样的好处在于,开发者自己的注意力和重心不用在整个功能需求内的小需求点之间犹豫,每次注重解决单个小问题,解决完一个进入下一个小问题的解决。在保证小粒度实现的同时,保证进度可以随时被打断,但同时被打断时已经做完的是完整可以运行,至少是实现了部分需求的实现代码。

而后写测试的代价是,首先实现代码很有可能包含设计上的问题,甚至含有缺陷,在完美的测试代码完成之前(事实上这是不可能的),可以交付的是可能存在严重缺陷,甚至是曲解了功能需求的实现代码。

Share
 

为什么要结对编程?

以下内容摘自myThoughtWorks中对于结对编程(Pair Programming)的讨论。

结对编程与非结对编程相比那个效率会更高?

这个问题没有一个明确的答案,没有人可以很肯定的说结对编程就是会比非结对编程更有效率。效率不单单是时间,还包含效果。

如 果对于一个技术难度不高,一个人几天就可以搞定,后期不需要维护的一个小项目来说可能结对编程在效率上会低于非结对编程。但是我们twer是关注代码质量 的,面临的项目一般都是需要持续发展,而且是具有一定规模的。对于这样的项目和我们,从长远来看,结对编程会带给我们长足的发展和更高的效率。对于这点我 总结了如下几点(欢迎补充):

  1. 代码质量。结对编程与非结对编程相比,往往更加容易写出更短 的代码,做出更好的设计,产生的更少的bug。俗话说三个臭皮匠顶过一个诸葛亮,每个人的思维 都是有瓶颈的,两个人的合作通常会考虑更多的设计选项,达成更简单,更易维护的设计。研究发现结对编程bug率降低会15%到50%,这个会根据程序员的 经验以及任务的复杂度而不同,这样就会大大减少以后修bug的时间,降低维护成本。这样我们才敢一个项目只有一个QA或者没有QA呀!
  2. 互相督促。 在结对编程的过程中我们可以相互督促,比如严格去TDD减少裸奔,提高注意力避免一个人注意力不集中或者花时间上网处理个人事宜,避免偷工减料 等等,从而也可以在某种程度上保证我们的代码质量。而且人们更不愿意打断两个结对编程的人,而单独工作的人却容易被打断,这样也增加了我们专注工作的时 间。
  3. 知识共享。在结对编程我们是知识共享的,还有我们会不停的switch pair,轮流与团队中的所有其他程序员结对编程,而不是仅与某个程序员编程,使得系统的知识在整个团队中传播,减少了程序员休假或者离开团队带来的风险,从而可以保证我们项目的可持续发展。
  4. 新人成长。 这是结对编程很重要的一个作用。曾经听大大说过,他和大熊发现,在别的公司毕业刚刚一年的毕业生在项目中可以承担的还很少,但是在我们公司很多 人已经成为了项目的骨干。这很大程度上要归功于结对。当新员工进入一个项目的时候,我们往往会安排有经验的老员工去跟他们结对,这样新员工可以更快熟悉项 目情况,更快的进入状态,更快的发现自己的不足,也可以学到更多的东西,更快的成长起来为项目做出贡献。
  5. 沟通能力。结对编程需要两个人一起讨论,一起把一件事情做完,也是对我们沟通能力的一种锻炼。让我们不再是很多人眼中只会闷头写代码的木讷的程序员了。
  6. 结对可以省钱http://www.infoq.com/cn/news/2009/06/dollar-value-of-pair-programming
  7. 徐昊说结对还可以Simple Designhttp://blog.vincentx.info/2007/03/agile-101-pair-programming-simple-design/
Share
 

DevOps是什么?

DevOps是什么?

devops is an emerging set of principles, methods and practices for communication, collaboration and integration between software development (application/software engineering) and IT operations (systems administration/infrastructure) professionals. It has developed in response to the emerging understanding of the interdependence and importance of both the development and operations disciplines in meeting an organization’s goal of rapidly producing software products and services.

— from Wikipedia, http://en.wikipedia.org/wiki/Devops

简单的说,DevOps是一组原则、方法和实践的集合,用来改善软件研发和IT运维之间的沟通、协作和集成,从而让软件产品和服务以更快的速度发布。

这里的快不是真正的目的,真的目的在于终端用户不会察觉到快速交付带来的不爽(质量、性能、安全性变差),而是享受到版本变化带来的更优质服务,这样才能让软件企业立于不败之地。至于为什么要快,我不说,你懂的。

一边要快,一边要稳定。说到稳定,就没法把运维避开不谈了。所以DevOps就是试图消除软件研发和运维之间的壁垒,让二者结合为更高效的软件交付团队。

DevOps涉及到什么?

DevOps是基于Agile的理念,并试图用Agile去影响其他部门、团队和实践。这里涉及到Agile和Lean中各式各样试图提高效率、消除浪费而采取的过程式、理念式、工具式的实践。可以包含但不限于以下内容:

  • 让开发人员和运维人员交叉参加部门会议
  • 让开发环境与运维环境保持一致
  • 自动化任何运维过程可以自动化的过程

如果有人或者公司说,用了我们的blabla产品,就是DevOps了,这只会引来明白人的窃笑。这个效果,基本和说我们每天都Stand Up所以我们就是敏捷也可以跳舞一样可笑。

在我看来,实施DevOps最后要面临和解决的依然是组织和人的问题,这是文化和价值观信奉的差别,与具体的产品和项目无关。我甚至预见,DevOps因为会涉及到不同业务部门,而不像敏捷只会局限在研发单一部门内,反而更难实行。所以,可以想象,在怎样的情形甚至逼迫下,会有怎样类型的企业会去追求真正意义上的DevOps。

扩展阅读:

Share