精益之美甚于帕斯雀(摘自《代码之道》第2章)

(译者注:帕斯雀(Pastrami)是这一种肉的专有名称,熏牛肉的一种,一般取牛肩部的肉来制作。国内的批发商普遍称之为胡椒熏牛肉火腿。)

曾经走在一个公共场所,比如机场的出入口通道或公园,突然一群狂徒冲你而来,想要说服你、或者威胁你、或者攻击你的无知。跟他们当中的任何一个人谈话,逻辑和推理都失去意义。在他们眼里,只有疯狂的信仰和不容辩驳的真理。即使你完全赞同他们,你也仍然没有机会去提问或分析。你必须深信不疑,无法提问,哪怕只提一个小小的问题。

这让我很不爽!我的意思是说,我真的全身心的不爽!我有自己的头脑,我想完全使用自己的头脑。不是只在聚会或者社交场合,而是包括我关心的所有主题和行为。我热衷于问一下“为什么”,并搞清楚事务的运作方式,这样的我才是真正的我。

你可能会认为,我这样的敏感性是给软件开发者的标准,因为他们无法去调试他们所不理解的东西。但一些致力于宗教、政治斗争和环境保护的人,他们在一些开发者的指引下也采纳时髦的开发实践,比如极限编程(XP,eXtreameProgramming)、敏捷方法和团队软件过程(TSP,Team Software Process)。他们拥有一样的热情!

任何事情都要适度

我非常喜欢这些开发范例提出来的很多思想和方法。但面对一个虔诚的追随者,如果我问为什么要做某个特定的事情,或者当我建议对规则或实践做一个很小的改动,以使它更适用于我的实际工作的时候,那就要当心了!这好比把一只魔戒摆在哈比人的面前——他顿时尖牙露出、头发根根竖起。对于一些开发者来说,极限编程和敏捷宣言已经成为一种祭仪。而对于另外一些开发者来说,团队软件过程是一种忠诚度的度量——你跟我们非友即敌。

请原谅我的务实。原谅我使用自己的头脑。原谅我不迷信魔法,而坚持去做有用的事情。我不会因为“你必须得这么做”而去做。我的行为原则是,要对为什么这样做会成功,而用其他方法就会失败,两方面都能得到相当充分的理由。

俭则不匮

这引起了我对精益的兴趣。是的,请看本文的标题。虽然在极限编程、敏捷方法和团队软件过程中有很多奇妙的东西,但至少有一个概念它们是共同的:减少工作浪费。这恰恰是精益设计和制造的焦点,而精益是源自于丰田汽车公司的一个概念,它比极限编程、敏捷方法和团队软件过程要早30多年。当极限编程、敏捷方法和团队软件过程以不同的方式试图解决这个问题的时候,我们可以通过使用精益模型来更好地理解它们各自采取了什么措施。

因此,冒着惹恼一些敏感狂热者的危险,让我们展开来看一看。精益聚焦在以最少的工作浪费来向客户传递尽可能多的价值。要做到这一点,它采用了拉动模型和持续的改进。拉动模型说起来很简单,“没有需求,工作就不开始。”这就减少了无用的、不必要的、无价值的工作。持续改进聚焦在减少浪费和建立顺畅的客户价值流上。

精益定义了7种类型的破坏客户价值流的浪费:

  1. 过量生产
  2. 运输
  3. 多余动作
  4. 等待
  5. 过程不当
  6. 库存
  7. 缺陷

这些显然是制造业的术语,是吗?它们不可能跟软件有关联,是吗?显然你太年轻无知了。所有这7种浪费都直接跟软件开发有关。它们是软件开发的7宗罪,下面我会逐一“宣判”,同时指出极限编程、敏捷方法、团队软件过程以及一般的常识是如何来规避它们的。

过量生产

第一个极度浪费是生产得太多,超过了真正需要的那个数量。但愿这永远也不要发生。有没有一个产品在出货之前没有被削减任何已经定义并实现好的功能?有没有一个产品在出货之前没有保留客户永远都不会使用到的功能?产品太复杂、太全面、太注重扩展性、太花哨、太累赘、太费解……过量生产太恐怖了,它会导致难以置信的浪费。

极限编程通过短而紧凑的迭代来解决这个问题。它坚持与客户的持续交流,以及和开发者之间的持续沟通。这保证了所有人都知道其他人在干什么,并且客户总是有较高的认可度。结果,几乎所有完成的工作都是对客户有价值的。

敏捷方法是精益实践的一个集合,它包括极限编程。因为敏捷更多的是一种联盟,它不是一种具体的方法,而是给开发提供了很多种有趣的方法。其中有一种就是叫作“Scrum”的项目管理实践(“Scrum”是根据橄榄球术语来命名的)。开发团队经常与客户代表碰头,通常是每30天,以展示工作进展、重新安排工作的优先级以及进行过程改进。跟极限编程一样,团队成员也每天开会更新各自的进度和讨论阻碍工作的问题。

通过每月对工作优先级的重新安排和每日对工作的重新组织,一个Scrum团队把自身的关注点锁定在了对客户重要的东西上面。几乎没有任何工作是浪费的。通过聚焦在定期进行的过程改进上,价值流可以经常性地被优化。

走向深处

当然,你也可能会不得当地使用Scrum和极限编程:你首先工作在“基础设施”上,而让客户苦苦等候着他们想要的价值。为了得到客户经常性的反馈,快速迭代有个基本前提:开发应该“深度优先”,而不是“广度优先”。

广度优先极端情况下意味着对每一个功能进行定义,然后对每个功能进行设计,接着对每个功能进行编码,最后才对所有功能一起进行测试。而深度优先极端情况下意味着对单个功能完整地进行定义、设计、编码和测试,而只有当这个功能完成了之后,你才能去做下一个功能。当然,两个极端都是不好的,但深度优先要好得多。对于大部分团队来说,应该做一个高级的广度设计,然后马上转到深度优先的底层设计和实现上面去。

这正是微软的Office功能小组的工作方式。首先,团队对他们需要哪些功能以及如何把这些功能放在一起做出计划。然后大家被分成多个小型的多工种团队,每个团队自始至终一次只负责一个功能。结果是,一个完整实现并且稳定的价值交付给客户演示的周期要短得多。

深度优先通过把注意力放在将被使用到的工作上,而不是可能永远不会被客户关注或者几乎不会得到稳定化的“基础设施”上,这样就能减少过量生产。另一个出色的深度优先开发方法是“测试驱动开发”(TDD,Test-Driven Development),但我想在“过程不当”那一节中再展开讨论。

运输

第二个极度浪费是等待一些尚未到达的东西。在制造业中,这通常是指部件的运输问题。对于软件来说,这个“运输”指的是团队之间的可交付成果的传递。这里有3个令人厌恶的运输问题之源:构建、分支和E-mail。

构建(build):构建花费的时间越长,浪费的时间就越多。我需要告诉你的是:极限编程和敏捷方法都坚持每天构建。但对于巨型团队来说,每天构建越来越不现实。这确实是个大问题。不容忽视!

分支(branch):微软使用Source Depot。它对整个公司的价值是巨大的。但它也像一只宠物象一样让人爱恨交加:当它还小的时候非常可爱,但几年之后,你要经常给它喂食并伺候它,你的活动自由受到了限制。建立代码分支是个很好的主意,因此很多大的团队都这么做了。现在假设你在A2.B3.C1分支上工作,而你的伙伴在A3.B1.C2上实现了一个关键功能或者修复了一个重大Bug,他需要把他的代码改动反向从C2到B1、再从B1到A3进行集成,之后你还需要把他的改动从A3到A2、再到B3、最后到C1进行集成。天哪!!!等所有这些集成都做完了,草都快长出来了。推荐的解决方案是,为你当前的产品线在发布期内建立同一级分支。

E-mail:最后一个运输噩梦是E-mail通知:项目经理告诉开发和测试人员规范书准备好了;开发告诉测试说编码完成了;测试告诉开发说他们的工作被某个Bug阻碍了;开发告诉项目经理说他们的工作被某个设计变更阻碍了;还有我的个人偏好:在客户和依赖方或者卖主之间的任何沟通,特别是越洋沟通。极限编程和敏捷方法通过废除角色和建立每天会议制度来解决这个E-mail通知问题。但对于远程的卖主和依赖方,这行不通。眼下我们必须尽可能依赖自动通知,必要的话使用Live Meeting;如果使用E-mail,也要在E-mail中清清楚楚地给出对方预期得到的答复,减少E-mail的恣肆蔓延。

多余动作

第三个极度浪费是花时间去找东西。在制造业中,这是对机械和人的动作的一种浪费。在软件世界中,是指花在搞清楚要做什么、走向哪里和怎么去解决的时间。糟糕的搜索技术是动作浪费的一个典型例子。不可测试、无法维护、无法管理的代码同样是这种浪费。

使用断言和验证输入参数有助于快速发现Bug,并减少动作浪费。设计复审、代码复审、代码分析和单元测试都能达到减少浪费的目的。极限编程甚至建议“结对编程”(Pair Programming),但我个人认为,这样做是一种对资源的浪费(除非开发人员是在一起学习一个新的代码库)。团队软件过程度量你的所有活动和缺陷,你可以清楚地了解到你的时间是怎样被花掉的,因而你也能大大地削减你的多余动作。

一个特别讨厌但能避免的多余动作是为代码注释、Source Depot、Product Studio和代码签入邮件等复制Bug修复信息。每个人都浪费动作去管理Bug和项目进度数据的多个副本。有一些工具可以让这类事情变得简单,信息只需输入一次,便能自动被传播到其他所有的地方。这类工具应该用来减少动作的极度浪费。

等待

第四个极度浪费是工作上面的等待。前面谈到的运输问题只覆盖了在构建、分支集成和及时沟通方面的一大部分等待。但等待远远不止这些地方。最常见的死区是,团队在功能的优先级顺序上达不成一致,或者即使达成了一致但没按照既定的顺序去实施。也就是说,产品经理如果胡乱地写规范书,开发人员就不得不等待;开发人员不按顺序去实现功能,则测试人员只能等待;测试人员不按顺序去测试,那么所有人都必须等待。

极限编程、敏捷方法和团队软件过程都强调让团队决定优先顺序,并且得到客户或者他们的代表的签字认可,然后再按照那个顺序依次开展工作,直到他们决定重新审核优先级为止。团队软件过程在这方面尤为严格,但在没有灵活的领导者的情况下,计划也会缺少迭代性。

此外,不稳定的代码也会导致等待。只要代码不稳定,测试团队就不得不等待,其他用于得到客户反馈的机制也得等待。极限编程和敏捷方法特别关注可验证的稳定代码,这是深度优先策略的又一个要点。

过程不当

第五个极度浪费是工程过度。你可以看到它们通常表现为如下形式:开发极度复杂的功能,在已经表现足够好或者不是真正瓶颈的地方继续提高性能,还有增加不必要的泛化或者扩展性等。这种浪费跟过量生产有关,但这里更强调在具体功能的实现上面。

药方:测试驱动开发(TDD,Test-Driven Development)。这是一种极限编程和敏捷方法为实现设计提供的方法。作为一个副产品,这种方法同时也产生了完全覆盖代码的单元测试。其过程相当简单:

1.        定义你的应用程序接口或者公共类方法。

作者注:这是一个我和敏捷社区的一些成员起争执的地方:你是在写单元测试之前还是之后写应用程序接口或者公共类方法?偏执狂说之后写,但我认为要之前写。两者不同的是:前期设计的程度,以及你跟依赖你代码的外部组织之间的关系。我相信,在10万代码行级别的项目中适度的前期设计是成功的关键要素。

2.        根据一个应用程序接口或者类的需求写一个单元测试。

3.        编译并构建你的程序,然后运行单元测试并确认它失败。(如果成功了,则跳过第4步。)

4.        只写足够的代码以使单元测试通过。(同时要保证以前所有的单元测试仍然能够通过。)

5.        重复第2步至第4步,直到应用程序接口或者类的所有需求都测试通过。

很自然,当你掌握了这种方法的窍门之后,你可以一次为多个需求写多个单元测试。但当你刚刚起步的时候,最好还是一次只做一个。这样能够养成良好的习惯。

当你使用测试驱动开发方法时,你不会比绝对需要的再多写一点代码。你也自然而然地得到了很容易测试的代码,并且这些代码通常还是强内聚、松耦合、少冗余的——所有这些都是真正的好东西。哦,我曾提到你也能得到完全覆盖代码的单元测试了吗?你还有什么不满意的吗?

库存

第六个极度浪费是没有交付的工作和产品。这跟削减功能有关,但它也包括那些正在进展中的工作。当你采取宽度优先的开发方法时,你所有的工作同时开展,直到代码编写完成并完成稳定化。所有完成的规范书、设计和等待通过测试的代码都属于库存。它们的价值都尚未实现。

尚未实现的价值是一种浪费,因为你不能把价值演示给客户和合作伙伴看。你不能得到他们的反馈。你不能改进和优化客户的价值流。当然,如果产品计划改变了,这些尚未实现的库存通常就变成了巨大的工作浪费。

精益拉动模型强调只做需要做的事情,因此它的结果就是低库存,这在Scrum和测试驱动开发方法中得到了很好的验证。Scrum特别关注正在进展中的工作,时时跟踪并努力减少它。Scrum同时利用定期的机会去改进和优化你传递价值的方式。测试驱动开发方法要求你只实现满足需求的代码,多实现一点点都不要。

缺陷

第七个极度浪费是返工。这是最明显的一个,也是我过去批判得最多的一个。极限编程和敏捷方法通过各种方法来减少Bug和返工,这些方法包括测试驱动开发、每日构建、持续的代码复审和设计复审等等。

然而,极限编程和敏捷方法也以一种更为微妙的方法来减少Bug——通过建立一个能让你边走边学的框架。使用深度优先开发方法,在你为整个产品完成设计和编码之前,你一点一点地理解了项目的各个部分。这避免了严重的架构问题,而且通常这种架构问题隐藏得很深,等到被发现的时候已经太晚,不容许再调整了。听起来很熟悉吧?

减少缺陷是团队软件过程的专长。使用这种方法的团队能够使他们的Bug率下降到行业平均水平的千分之一。虽然团队软件过程本质上来说不是精益,但它也并不排斥深度优先的开发方法。

组合应用

是时候让我不得不激怒极限编程、敏捷方法和团队软件过程的虔诚追随者了。因为没有理由说你不能把这些方法组合起来达到更为出色的效果。使用Scrum来驱动一个精益、深度优先、灵活的、优化的开发计划。使用测试驱动开发方法来创建一个精益实现。使用团队软件过程来分析你的缺陷和工作,这将导致Bug和工作浪费的大量减少。这些对某些人听起来可能会怪怪的,但在我看来却是再合理不过的了。

我现在的感觉好像是刚刚找到了一块美味的帕斯雀牛肉。

时间: 2024-08-30 08:12:05

精益之美甚于帕斯雀(摘自《代码之道》第2章)的相关文章

转载▼当一切渐渐慢下来 加拿大(尼尔、帕斯理查)

当一切渐渐慢下来    加拿大(尼尔.帕斯理查) (2014-05-08 09:25:26) 转载▼     时间是一个幻觉. 宝贝,我们在旋转,旋转,旋转.电子在我们高大的血肉之躯里旋转,电子在又大又潮湿的岩石里旋转,电子在明亮皎洁的太阳里旋转.电子在深邃漆黑的星系里旋转,电子在无边无际的宇宙里旋转. 这种永不停息的旋转有时太让人犯晕,所以我们试图为我们混乱无章的生活,设置某种精巧的秩序.我们在厨房壁橱上挂起日历,划分出每日每周每月,划分出每日每周每月.我们策划着星期六晚上的活动.星期天早早熄

没公比什月斯有作打学切到带

进机立观拉地即声强号目信特写比利通更不各根适象毛因作从为证规口后料单规美连接造求始识等相求一美提活本最 使义备标道两小该层制派看发之形省感命选然听共心相己行变它过府在划去利单力国重领县清向带命整理四义参山们数即度何合清矿里达方同确她水象置温主分品意置无整新价己同方件此金关几取组问维 东界应存清片花特给习活实要平对联系传据约住则维片设这极干区大须也人全资知法养花组两走团改等子是都行选意点化结满术南学府度半务具理即象收林治往海总志新细研想养分然铁 想土满周装程年不育克据石根证得列些没按边二方京太己书

机器学习基础——带你实战朴素贝叶斯模型文本分类

本文始发于个人公众号:TechFlow 上一篇文章当中我们介绍了朴素贝叶斯模型的基本原理. 朴素贝叶斯的核心本质是假设样本当中的变量服从某个分布,从而利用条件概率计算出样本属于某个类别的概率.一般来说一个样本往往会含有许多特征,这些特征之间很有可能是有相关性的.为了简化模型,朴素贝叶斯模型假设这些变量是独立的.这样我们就可以很简单地计算出样本的概率. 想要回顾其中细节的同学,可以点击链接回到之前的文章: 机器学习基础--让你一文学会朴素贝叶斯模型 在我们学习算法的过程中,如果只看模型的原理以及理

精益生产篇(1)

精益生产的培训在期待的目光中如火如荼地进行了.参加的热情超出了我的想象,本来我计划是40人参加,结果50人参加,上海下雨也是没有挡住这股学习的热情.而我们现场存在诸多的问题,老师也给指了出来,我与老师开玩笑说:"老师您是及时雨",老师开玩笑说:"的确是,我的名字里面正好有个'宇'字,宇与雨谐音也!",正是,忽如一夜春风来,千树万树梨花开.人生,有时候需要这样的老师给予我们指点,老师提供的建议和提点,正如春风般,润人心田.我们有改善的想法,可是我们没有老师给我们指点,

初探NO.3—从头到尾聊聊贝叶斯的分类方法

宅在家无聊之余决定看着<概率论和数理统计>&<统计学习方法>总结一下朴素贝叶斯和贝叶斯估计. 正好这一块我最近温习了一下,我从一开始条件概率开始写,把我所理解的贝叶斯分类算法完整呈现一下吧. 学的概率论最开始是在高中,当时是条件概率,给出条件概率的定义:事件A在另外一个事件B已经发生条件下的发生概率.条件概率表示为P(A|B),读作"在B条件下A的概率". 其次我们有必要温习一下乘法定理,乘法定理的定义:设P(A)>0,则有P(AB)=P(B|A)

编程之美3.8 求二叉树中节点的最大距离

描述:如果把二叉树看成一个图,父子节点之间的连线看成双向的,定义“距离”为两个节点之间边的个数.求二叉树中相距最远的两个节点的距离. 思路:相距最远的两个节点一定是叶子节点,且这两个叶子节点的路径有两种情况: 1. 该路径经过root节点,则两个叶子节点分属root.left和root.right为根的子树,而且是两个子树中距离root.left和root.right最远的叶子节点: 2. 该路径不经过root节点,则这两个叶子节点的root.left或root.right上: 根据以上分析,参

x01.os.15: 看上去很美

张碧晨在韩国学的不是技巧,而是基本功:气息!声音由气息托着,似真声而不是真声,似假声又不是假声,所以才能在动听的地方唱得更动听.编程也是一样,基本功很重要:内存!所谓的黑客高手,攻击的一大手段,便是利用了内存泄漏. 纸娃娃 操作系统,看上去很美,效果图如下: 如果细究代码,发现只是一张皮而已.但内存把控能力,却不容小视. *((int*)0x0fe8) = (int)p; 这是干什么?完全是在操作内存啊!这样的例子,比比皆是.在程序 a.hrb 的讲解中,更干脆用二进制编辑器打开,直接操作了一把

算法之美---由计算机生成的图像

发几幅由计算机生成的图像,以展示算法之美.并提供生成图像的算法代码.代码中,一部分是由C++实现,另一部分是由一种我定义的脚本语言实现. 相关软件见:Why数学图像生成工具. (1)树 1 void CPixelIFSTree::BuildPixelsMap() 2 { 3 float m[5][7]; 4 5 //'IFS码赋值 6 m[0][0] = 0.195f; m[0][1] =-0.488f; m[0][2] = 0.344f; m[0][3] = 0.433f; m[0][4] =

精益文章(1)

在期待的目光精益培训是如火如荼.参与超出我的想象的热情,我本来是打算要40参与者.结果50参与者,上海雨也没有挡住这股学习的积极性. 和我们的生活,有很多问题,教师还手指出,我开玩笑说老师:"师您是及时雨",老师开玩笑说:"的确是.我的名字里面正好有个'宇'字.宇与雨谐音也.".正是,忽如一夜春风来,千树万树梨花开.人生,有时候须要这种老师给予我们指点,老师提供的建议和提点,正如春风般.润人心田.我们有改善的想法.但是我们没有老师给我们指点,则不知道怎么做,思路是模