Richard Feynman, 挑战者号, 软件工程
源文:链接 (本文主要根据挑战者号的问题,以及Richard Feynman那对NASA严厉的批评报告,批评了不适当的“自顶向下”的设计方法,并总结了一下软件工程和其它工程的相通的一些观点。翻译水平有限,欢迎指正)
佛罗里达州,美国东部时间1986年1月28日上午11时39分,挑战者号航天飞机 执行为期6天的STS-51-L 任务,在发射后,其右侧固体火箭助推器(SRB – Solid Rocket Booster)的O型环密封圈(用于连接两节助推器)失效,泄漏出来的热汽达到了5000华氏度,直接蒸发了O型密封圈,并灼烧了毗邻的外部燃料舱,在几秒钟内,外部燃料舱出现结构连接失效,空气的动力迅速分解了航天飞机。在而航天飞机上升72秒以后,助推器脱落,导致航天发飞向侧面滑出。几乎在引航员 Michael J. Smith 发出”Uh oh” 的同时,整个航天飞机完全解体,片刻,航天飞机内部发生爆炸,所有7名宇航员罹难。 那时的我还只是一个小孩,我从电视下方滚动的新闻条目知道了这一惨剧。
在那个时候,火箭助推器工程师曾经警告过这个O型环可能存在问题,但可惜的是,NASA的管理层忽略了这个问题。美国总统里根委派罗杰斯委员会对事故进行了调查,调查成员包括著名的物理学家Richard Feynman。其不羁的态度和直来直去的方法和罗杰斯委员会的风格形成了鲜明的反差。主席罗杰斯,一个政客,评论Feynman是一个“真正的痛苦”。最后,在委员会提交的报告中,Feynman反判的观点几乎被清除了出去。并且,Feynman曾被主席威胁过要把他的名字从报告中完全除掉,但最终,他们还是同意在报告中加一个附录,但只是个人观点—— Appendix F – Personal Observations on Reliability of Shuttle。
这是一个好的报告,因为,这是一个富有才华的报告。其深深地洞察了在实现一些高可靠性的系统时的工程学中的一些很自然性的东西。是的,在这里,我并没有放上“软件工程” 的字样,只是工程。但Feynman的结论却非常和我们的软件开发有着不可分割的关系。这是最基本的东西,无论是软件工程,还是别的工程学。下面,让我们来看看,Feynman是怎么说的:
航天飞机主引擎的建造方式是自顶向下(top down),我们可以这样说。整个引擎被设计把所有的事情放在一起,而那些相关的细节上的东西在设计当时还并不是很成熟的。所以,当其中的小零件(轴承,涡轮片,散热管,等等)出现问题时,我们需要花费昂贵的代价才能找到事故的原因,也很难作出修改。要避免问题发生,需要频繁的维护和置换重要的零部件。修理很多时候不会解决真正的原因。
可见,软件开发中也一样,Bug在整个过程中存在的时间越长, 我们就越难解决这个问题。很显然,自顶向下的方法,因为在设计的时候并不熟悉实际问题,所以,Bug从设计的时候就出现了。然而,我们需要明白,需求和设计的不同之处。需求需要对产品一种清楚和良好的定义,设计则是解决如何达到需求的方法。Feynman 在这里并没有反对 功能规格说明书,他只是反对自顶向下的设计方法,比如: UML 就是蓝图 的鼓吹者。再来看看他的言论:
航天飞机主引擎是一个非常不同寻常的机器,它和以前所有的引擎都不一样。这完全超出了以前引擎制的工程经验。所以,不奇怪的,许多不同的流程和难点都会在工程中出现。然而,很不幸地,这是通过自顶向下设计,所以,那些流程和问题是很难被发现被修正的。设计要求的引擎寿命可以完成55次点火任务(相当于27,000秒的操作,也就是说,第次点火需要500秒),但事实上这并没有完成。而引擎现在则需要频繁维护,并需要经常更换重要的部件,比如:涡轮泵,轴承,金属片,等等。
“不合适的自顶向下的设计方式,导致了问题很难去发现和修正,最终没有完成设计需求,频繁性地维护”这些描述方式,听起来是不是似曾相识?我们每天在做的软件工程和这个不一样吗?Feynman 详细说明了为什么“自顶向下”的设计会让发现和解决这些问题成为那么的难和痛苦的一件事:
很多这些已被解决的问题在一开始设计时都是设计的难点。很自然地,没有人可以确定那些所有的已发现问题都能会出现,而其中一些,我们并没有根据正确的原因在正确的地方解决这些问题。
无论这是Linux内核,或是航天飞机引擎,这些设计时的基本的问题都是相通的。而“自顶向下”是其中荒唐的一个,因为,自顶向下,过度的注重了需求而忽略了现实,而那些下面非常细节的知识绝对是非常需要的,并不是所有的东西都可以抽象成出来。在他说起航空电子系统时(一个NASA的另一个部门):
该软件是采用了从底向上的方法被小心地做了检查。首先,每一行代码都被检查过,然后,代码段和模块和一些详细的功能被验证过。而检查范围在一步一步地被扩大,直到新的改变被组合进来最终成为一个完整的系统。这个过程最终的完整的输出成为了最终的产品,成为了新的release。这个部门完全以一种中立的态度,把软件作为一个敌对方,不停地测试,校验,就像自己就是这个软件的用户一样。
是的,这就是1986年Feynman告诉大家的——Unit Test(单元测试),今天,Unit Test成为了软件开发活动中最最重要的一个环节(也许你以为是Coding)。并不单单只是Unit Test,“步步为营的增量式”和“以敌对的态度”,都是值得我们所学习的。我们经常听到有人在抱怨软件道,因为软件工程还太年轻了,还有很多知识我们还没有得到,所以总是那么多问题。这完全是胡说!我们痛苦是因为,我们 总是忽略 早就确定了的, 早为人所熟知, 以经历和实践去证明一切的方法。 当然,在这方面,我们的管理层也需要负责,尤其是那些紊乱的时间进度,错误的激励机制,低档次的招聘,和一些让士气受挫的制度,等等。“管理”和“工程”间的紧张关系最终成为了糟糕的管理。Feynman在他的报告中也谈到了这点,下面其中的一小段话:
总而言之,计算机软件检查系统和最负责的态度。是的,那里并没有那种自欺欺人而不顾固体燃料助推器的标准。但可以肯定的是,有关管理部门最新的建议,建议取消此类复杂而昂贵的不必要的测试。
这只是其中的一个小段。我把其挑出来是因为其一针见血地指出了观点,比如“最负责的态度”,以及“逐步的自欺欺人”。我建议你读一读报告全文, 可以让你得到很多真相。关于软件工程,下面是几个主要观点:
- 工程仅当在和其管理有好的关系的时候才能好。
- 大型的从上从前端的设计是愚蠢的。
- 软件工程和其它传统的工程学是一样的。
- 可靠的系统由几近残酷的测试,增量式的自底向上的工程,以及高负责的态度来共同保证。
这篇报告中,还有很多不错的观点,如果你感受到了,欢迎你告诉我。
(全文完)
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《Richard Feynman, 挑战者号, 软件工程》的相关评论
自顶向下的缺点是显而易见的,但是又是不可避免的,再好的蓝图在实施过程中就会出错,只有不断的对照调整才会将错误降到最低,然而这又影响了工程的进度。一个好的思想是”大事化小”,将一个工程划分为好几个同级别的子工程,每个工程互相独立,采用自定向下的思想开发,结合unit test。
思想固然重要,不顾良好的组织结构和优秀的执行能力才是事情成败的关键。
不自顶向下,怎么把一个复杂的事情分解为可以独自进行的小事情?
分解时,有些细节尚未准确定位、解决,是很正常的,需要独自进行时由专门、对口的人来定位、解决
“可靠的系统由几近残酷的测试,增量式的自底向上的工程,以及高负责的态度来共同保证”
深有同感
之前在 Growing Object-Oriented Software, Guided by Tests 這本書看到一個不錯的作法, 自上而下設計, 但是自下而上 TDD 實作。先有個可動雛型, 再不斷重覆翻修整體架構。換句話說, 先來個 v1 自上而下的設計, 從底層實作回來一層子。再來 v2 自上而下的設計, …, 如此循序漸進改進整個系統。雙向夾擊的作法感覺還不錯
设计本来就应该是自顶向下的,先有一个整体的认识和规划,然后逐步细化分解。否则一开始根本不可能知道需要什么样的细节实现。就比如在还不知道需要一个什么样的航天飞机发动机的时候就无法知道在某个具体部位需要一个什么样的螺丝。
测试应该自底向上进行,先从细节处验证设计和实现的正确性,然后验证合格的部件组装起来能否正常工作。
发现问题时是需要用测试来定位错误而不是设计。
完全是在扯淡的。不要因为别人文章的观点和你的部分重合就认为他的观点是你所有观点的佐证。
我們需要自頂向下的設計規劃,但我們也必須了解透過iteration去讓設計貼合現實、早期分析出工程技術難題,並確立滿足『解決該難題到可接受的地步』的測試該是什麼樣的。
而這一切又會回饋到總體設計裡去,於是在第二個輪迴,我們又要透過分析、重新的去問同樣的問題。
而我們總是可以發現問題的範圍改變了、問法不同了、甚至拆解了,於是我們距離成果就更近了些。
TDD只是站在不同的角度看待问题,TDD至少还没有鼓吹说是什么设计方式,只是一种编写代码的一种方式,提供一个较好的角度,和解耦方式而已。而且挑战者的失败,就进行攻击TDD,似乎以偏概全了。现代软件工程和软件工艺是基本上很大差异的两个领域,工程和工艺,TDD适合工艺,工程上的大规模项目,请参加NASA各种项目,