写点什么

软件开发软技能:“从无意识的故障中学习”模式

2018 年 1 月 03 日

本文要点

  • 软技术模式是经证实可解决常见问题的个人和人际交互行为的组合。
  • 系统故障几乎不可能完全避免,但同时每次故障也都带来了改进的机会。
  • “从无意识的故障中学习”模式指导我们在故障事件后改进系统的弹性。
  • 该模型有四个独立的步骤:识别故障、快速解决即时影响、分析根本原因和故障期的系统行为,最终形成并实现改进思路。
  • 召开事件分析会时必须开放、坦诚、不加责备,这样才能促成借助故障改进系统弹性。

什么是软技术模式?

软件开发人员需要强大的软技能以有效地解决我们所面对的许多问题。

Peter F Drucker ,著名的管理教育家,他告诉我们,“做正确的事比正确地做事更重要。”直观上,这句格言说得很对。对于程序员来说,做一个没人需要的伟大产品又有什么价值?

软技能,包括交流、协作,以及解决问题,界定了我们“做正确的事”的能力。我们的硬(技术)技能只能帮我们“正确地做事”。所以,从有效交付价值这一方面来说,我们的软技能自然比硬技能更重要了。

自 1995 年“四人帮”向我们提出“设计模式:可复用面向对象软件的基础”以来,软件开发人员就已经清楚知名模式的好处了。我们知道,尽管我们所遇到的问题不可能完全一样,但经常还是能从中找出共同点的。

一旦我们找出这样的共同点,就能够把实证有效的解决方案转换成确定的可复用模式了。这些模式不仅帮我们有效地解决常见硬技能问题,还可以降低我们的决策时间,提升对解决方案的共同理解。

那么,如果我们可以从解决硬技能问题的模式中获得收益,能不能也从软技能问题获得同样的收益呢?

在本文中,我们将看一下能帮我们从下述系统故障中促成重大改进的“软技能模式”。我们将步入“从无意识的故障中学习”模式。

为什么是这个模式?

软件系统有时就是会出故障,这是无法回避的现实。这些故障会影响系统的用户,于是开发人员的首要目标就成了把故障及其影响降到最低。幸运的是,每个故障也都同时带来了改进系统弹性的机会。

“从无意识的故障中学习”的模式是个四步法,其中先识别无意识的系统故障,尽可能快地解决以降低影响,然后进行分析以确定根本原因,基于分析结果得出改进思路并予以实施。

这个模式看上去说得非常直白,很多人乍一看都觉得平平无奇。然而,只有有效彻底地分析,把改进思路落到实处执行,才能从这种做法获得实际的收益。这种模式说了一种在系统故障发生后获得实际系统提升的有效方法。

从无意识中学习的实际案例就在我们身边。防洪堤、建筑物里的自动喷水灭火系统、保险丝,还有汽车里的安全气囊,这些只是人类对之前故障作出回应的一小部分案例。

什么是“从无意识的故障中学习”模式

“从无意识的故障中学习”模式描述了一个确保从系统的非预期故障中获取最大价值的有效方法。为了这个模式的目的,考虑系统涉及的任何基于软件的解决方案或产品。

这个模式的灵感是受到了 Matthew Syed 的《黑盒思考》一书的启发。Syed 介绍这本书说:“……它讲的是积极自发地研究那些发生故事时常常存在但我们又很少予以开发的经验教训”

事实上,说每次系统故障都可以避免纯属事后诸葛亮。然而,故障在所难免,即使做了最大努力的准备,有些故障仍然有可能发生。这个模式帮我们从所有严重的故障中获得有价值的经验教训:从未遂故障到已造成严重运行中断。在广泛关注的故障中通常都会浮现出可显著改善系统的新思路,用心去发现吧。即使用于未遂故障和一般故障,这个模式也能形成改进思路。这些思路会对系统弹性有很大帮助,只要它们得以落实。

怎么使用这个模式

让我们来看看怎么使用这个模式,一步一步地来。你将发现该模式中的每一步在上文图表中都已经定义好了。我将重点介绍每一步中最主要的软技能。

第一步:系统发生故障

这是此模式的切入点。系统以非预期和非最佳的方式运转。这可能就会导致对系统使用或输出的负面影响。以下步骤将帮助你解决这个问题,并从长远出发改善你的系统。

第二步:解决故障导致的即时影响

如果这个故障导致了负面的影响,那么就应该立即予以解决。“破损系统的危机处理”模式针对这个行动详细详细描述了一个有效方式。需要重点考虑的是确保任何行为不带来更糟的影响。

在这一步中,将应用的软技能包括:冷静、问题解决技能、风险意识、协作和交流技能。

一旦系统恢复到正常状态,你就可以前往下一步了。

第三步:分析什么出现了差错:根本原因和系统的行为

在这一步,会用到许多的软技能。需要执行根本原因分析以理解什么导致系统出现了故障。需要用逻辑思维和分析思维去理解在故障期系统到底有着怎样的行为。使用协作能力发挥更多人的力量。

有许多理解和实践可用于执行根本原因分析。研究系统故障时,经实验证明有个称之为“五个为什么”的方法通常比较有效。维基百科为这个“五个为什么”法提供了简单的示例:

  1. 汽车打不着火了。(问题)
  2. 为什么?电池不行了。(第一个为什么)
  3. 为什么?交流发电机不运转了。(第二个为什么)
  4. 为什么?交流发电机履带断了。(第三个为什么)
  5. 为什么?交流发电机履带超过使用寿命后没有更换。(第四个为什么)
  6. 为什么?汽车没有按照建议的售后日期保养。(第五个为什么,根本原因出现)

“五个为什么”确保充分深入地研究,以找出实际的根本原因,而不只是浮在表面上的影响。修改这些影响是很有必要的,但是解决根本原因能得到更多的好处。在上例中,我们应更换交流发电机的履带,而不应该只解决这个具体的问题。修正根本原因,按期保养,会得到更多的好处。

找出根本原因能避免同样的故障再次发生。在故障发生时,把系统行为彻底想清楚可能会得到更多的益处。根本原因让你的系统按反常的方式来运转。修正根本原因也就掐死了进入这种反常方式的入口。分析反常方式本身经常为带来额外的改进机会,比根本原因覆盖更多的可能性。

为了理解故障发生时分析系统行为时可能带来的好处,让我们假设正在森林远足。因为我们不了解这一区域,所以得依赖些路标。不久,我们到了一个路口,它的路标牌倒在地上。我们不清楚该往哪里走了。现在,我们就处于了反常方式,做些什么呢?根本原因很清楚,那就是路标倒了,但我们的行为将决定其影响。如果我们选择了错误的道路,或者惊慌尖叫着到处乱跑,可能就想回到原路了,认为“回到之前的路上可能会更好”。我们得到的经验,不是出自于根本原因,而是出自于对行为的分析。

第四步:在经验总结的基础上进行改进

在上一步,你找出了故障的根本原因,对故障发生时的系统行为有充分的了解。在这一步,你将使用这一新的经验去形成改进思路,决择实现其中的哪些思路。最后的环节将是试验并度量改进思路的效果。

形成思路

在许多情况下,根本原因和系统行为会带来明显的改进思路。继续延伸上一步中提到的汽车的那个例子,通过分析清晰可见,除了替换履带之外,我们还需要按时进行保养。

通过定义故障期间希望系统采取什么行为,也可以形成改进思路。有这么一个例子,对一次系统故障进行原因分析,得出结论是因为你仅有的服务器上的硬盘驱动器坏了。在这种情况下你可以发现,如果有第二台“热”服务器在跑的话,故障带来的影响将得到极大地降低。

形成的思路不应局限于实体的系统,也就是运行中的代码。改进系统被使用的方式,以及分析系统故障时识别易界定故障的能力。

思路的形成应具有创新性和“远大梦想”。要了解一些约束,如成本、时间、所需的技能等,但不能被其束缚。

选择要实现的思路

选择要实施的思路时,你可以最终选择一个、一些或所有思路,也可以一个都不选。选择要实现哪个思路时,需要做三个主观测验:

  • 其预期收益是否会高于预期的成本?
  • 你相信这个思路可以得到有效地实现吗?
  • 有高级管理者支持这个思路,可以为也这个思路的实现清除障碍的吗?

如果任意一个测验回复为否,那么就应该放弃这个思路。至于尚存的思路,还要通过一项最终测验:你们有资源实现所有这些的思路吗?如果没有,就使用竞争分析敲定最终的选择。

实现选择的思路

只有实现了思路所描述的预期变化,才能从思路中得到价值。在真正实现之前,思路只是组织的成本而非收益。就像买了本从未阅读的书一样。

按这个模式来做,选中的只是那些有最大可能实现的思路。通常,最难的一步就是迈出第一步。就像书架上那些你还未阅读的书一样,翻开第一页看起来总是那样艰难。果断开始动手吧。如果你们遇到障碍,就请支持这个思路的高级管理者去解决它们。

度量和检验取得的价值

有效的开发过程会不断地评估是否正在实现预期价值,并有针对性地调整整个过程。从改进思路中实现价值也没有什么不同。定期重新评估这三项测验:这个思路仍然具有收益、价值和支持者吗?随机应变,如有必要,也可以放弃它。

当一个思路已经实现时,就该评估取得的收益了。有两种评估方法可以用于评估和检验。

尽可能客观地比较相关度量值的前后变化。以量化数据比较清楚地展示这一变化带来的价值。比如这样的一个度量,“在这个变化之前,系统可以处理 10 个并发事务。而现在,它可以处理 100 个并发了。”

量化度量也不是总有效。也可以通过测试一个特定的场景来证明价值。例如:“以前,错误通常是由第一个受到影响的用户发现的,这往往会带来投诉。改变之后,所有系统错误都由系统来捕获,并立即自动地把短信发给支持团队。这就有机会在影响到用户之前就予以修复了。”

结果 1:改进系统弹性

遵循这一模式得到的主要结果是现在系统比故障发生前更有弹性了。按照这一模式中的步骤来做,应可以帮助你找到改进思路,选择最有收益的那一个,并通过实现它们得到价值。至少,系统再次遇到相同的问题时能有更好的结果。当然,获取的价值依赖于实现思路的数量和效力。

结果 2:更充分地理解系统

这一模式的步骤需要你深入理解你的系统在正常和非正常情况下的行为。这个过程涉及到了许多的人,所以理解不仅要更加深入,还得更加广泛。更充分地理解很可能对下一步增强系统和改善过程产生积极影响。更好地理解系统,能让你可以更好地控制它。

结果 3:更有效的响应故障的文化

正如我们看到的,积极坦诚地处理故障能对系统带来重大改进。此外,如果组织有隐瞒故障的文化,大家为了怕受到责备而拒绝承认或找借口,那么就错失了学习的机会。失败驱动过程的重复模式将消除恐惧,让大家更加坦诚,推动继续前进。当然,坦诚的文化比推卸责任的文化能让人更加愉快地工作。

行动的模式

注意:本故事及其人物纯属虚构(除了我)。

我的手机在 1 点 24 分响了:那只意味着一件事。“Kevin,生产系统宕了——什么都不工作了!新加坡的用户非常生气。你们改过什么?”我们的支持团队领导慌乱地叫着。“早上好,ED…”,我尽量条理清晰、冷静地回复他,“…你看能否和 Fiona 一起开个电话会议,给我 5 分钟。我们看看问题出在哪里。”

我(Kevin,伦敦研发经理)、ED(纽约,支持领导)和 Fiona(新加坡研发经理)召开了一个电话会议。在其他人的帮助下,花了大约 4 个小时搞明白什么出了差错,然后把系统回退了一个版本。我们遵循“破损系统的危机处理”模式,将保留这四个小时的细节。这个“从无意识的故障中学习”的模式主要关注危机解除和系统恢复正常之后发生了什么。

不久之后,系统被修复了,Ed 和我收到 Gail 的邮件,她是新加坡销售团队的领导,我们系统的一位主要用户。邮件中说:

“新销售系统无法使用了,这是最近两个月里第 15 次了。怎么没测试过新的数据?我正在亲自做事件分析,一个小时了。打电话给每个与此相关的人。马上!”

我知道要发生什么了。很明显,Gail 对已经对发生了什么事和谁应该挨骂已经有了判断。那一刻,我都忍不住想按她说的去做了,毕竟,她发火的主要目标并不是我。Hardeep 是这次变更的开发人员。他帮我们发现并解决了问题。但是,他解释不了为什么在他执行的大量测试中没暴露出来。

我拿起电话小心谨慎地打给 Gail。我清楚我必须做正确的事,即使要冒招致负面责备的风险。“嗨,GAIL。对于这次分析会,我只想先简单地聊一聊。我们可以把它推迟 24 小时吗?在我们修复这个问题的同时,我发现了有些东西看起来有些不太对。它们可能解释了为什么 Hardeep 那么确信他适当地测试了变更。”Gail 听起来很激动,回复道,“真的?好吧。我现在有很多事得解决。明天,不能再晚了。必须做到。销售系统宕掉了我们还怎么卖东西呢?”

我们的销售系统从许多来源接收数据。Hardeep 开发的变更增加了一些新的字段到已有的营销数据的输入提要中(一个纯文本文件)。该输入提要是我们系统的关键,流程的其余部分都依赖这个数据。

根本原因分析确定了发生的问题,因为更新的数据提要中首次包含了一些以科学符号表示的值(就像 1.5E-5)。我们的生产系统因为“E”这个字符没把这些值看作数字,于是就报了错。在大量的测试期间没发现它,因为测试环境使用的数据与生产环境差异很大。在测试环境使用科学符号表示的值没出现问题。

我草绘了一张图,展示了变更之后正常的系统流程和实际做出的行为,以便会议期间使用。

在故障期对系统行为的研究得到了一些惊人的发现。首先,我们发现不良数据导致的这个错误没有被抛出,一直都到了整个过程非常靠后的阶段。因为数据是以非结构化方式存储的,所以数据加载过程并没受到影响。发现不良数据太晚了,也就是说没时间去修正它。第二,很明显整个系统依赖于这些数据。没有它系统就没法用。它不仅仅包括营销数据,还有很多其他数据。

Ed 在第二天主持了这个分析会。我们花了两个小时过了一下所有的细节。为使再次发生同一事件时能有更好的系统行为,我们给这张草图加了点变化。第一个变化是会立即捕获到异常并通知数据提供者。第二个变化是,如果他们不能在临界时间发送正确的数据,就让我们能够复用最近的好数据。有这两个变化,再次出现这个问题的影响就微不足道了,销售系统仍然可以使用。

会上对故障相关内容进行了总结记录,作为本次会议的结束,内容包括一条根本原因和一个解决措施列表。每个人都同意这些措施是我们当前优先级最高的事,比下一批要开发的特性更加重要。Hardeep 很高兴有机会向大家展示他费尽心血执行过的测试。甚至 Fiona 也对结果非常满意。在分析会结束时她提到“感谢每个人对此做出的如此认真的付出。我发现这不像我之前所想的那样,仅仅是因为懒惰造成的宕机。我有信心,这些措施能带来很大的变化。大家动起手来,实现它吧。”

故障总结

“九月九日在销售系统上做了一个变更,打算将四个新的字段添加到营销数据提要中。这次变更无意地修改了一些已有字段以科学记数法发送的值。这些非预期值被系统当成非数字来处理了。在日常批处理中试图读取这些值时没有抛出异常。但后续流程步骤却由于这些错误无法启动。因为日常批处理失败,销售系统就无法使用了。”

根本原因

“发生故障的原因是因为测试环境不太完备。因为测试和生产环境的差异,未在测试期间发现数据类型的问题”。

解决措施

  1. 环境:确保测试环境与生产环境一致,尽最大可能提升测试效力
  2. 数据质量:为所有数据提要加载过程增加一个校验,确保所有的值都符合预期的数据类型。发现任何不匹配时发送警告,予以紧急处理。
  3. 批处理依赖:修改日常批处理,如果新的数据不能及时有效,则复用前一天的数据。必要时采用旧数据总要强过无系统可用。

反模式:要避免的陷阱

在本例中,遵循该模式确保了一些良好的系统改进得以实现。在制定决策中使用该模式时,有几件重要的事应特别注意。

我们不犯错

我们赶紧制止这个神话吧。人人都会犯错,如果你是一名开发人员,犯的错就可能导致系统发生故障。关键是从错误中总结教训,不重复犯同样的错误。你所能做的,就是接受错误并采取措施。

学习之后不见行动

我经常见到这种反模式。首先是对危机的恐慌。然后是对解决方案兴奋不已。盲目乐观。以及最后一点:行动无优先级。直到下一次出现危机,由同样的问题导致的。以同样的措施进行修复。不言而喻,只有你真的行动起来这些伟大的措施才能真正帮到你。把它们丢在待办事项中什么用也没有。

得出不成熟的结论

对故障原因很容易就会做出一个过早的判断。这很难避免,所以给你个建议,先不要把这个判断与别人分享,直至实际上真的搞清楚了。公开发布过早的观点,经常会导致两种有害的影响。首先,人为带动其他人持有同样的观点。甚至它完全就是错的,公开声明观点也会让人当真,而有时它其实与事实相反。在玩计划扑克时,如果有人提前些打出了牌,会不会影响到你的选择呢?第二点,认知失调理论告诉我们,一旦我们把观点告诉其他人,再去改变这个观点的个人牺牲会非常大,以至于会让我们闭上眼不愿去看任何有矛盾的证据。在我们的例子中,把分析会推迟了24 个小时,让每个人在分享观点和下结论前有机会去收集事实依据。如果直接开会,很可能根本就得不出有价值的结论。

责备与自保

事实上,所有有价值的经验都来自于导致出错的故障。其实它基本与涉及到的人没有关系。当然,也有极个别过失或故意造成损害的情况。在这种情况下,就应该对个人采取相应的措施了。开始陈述你的事实观点时,评估一下是否存在过失或故意的情况。将精力集中在出错的东西和如何做出改变以避免其再次发生上。在流行责备的文化中,个体将回避深入研究故障,以求自保,若不这么做就会因为无法避免的错误受到责罚。这两种情况会使组织越来越软弱,从失败中学习的机会也就丧失了。

我们光忙着分析了。

你做过事后分析吗?如何没有,打算如何从这些故障中总结经验教训呢?是的,有效的分析得花时间,但是这几个小时就能发现系统的重大改进。不要在错误的方向上冲刺,在正确的方向上慢跑,你将更快到达终点。

简单化和从经验中吸取教训

软技能模式归根结底是通过从经验中吸取教训以找办法让工作更简单。我们自己的经验,和其他人的经验。将事情简单化使我们的工作和生活更有效率,并帮我们不断“做正确的事”。

我们可以使用模式去解决与软技能(交流、协作、问题解决)的问题,就像我们使用它们去解决硬技能问题一样。

考虑一下软技能模式的潜在价值,尝试一下“从无意识的失败中学习”模式。我希望,如果你发现它对你有用,你也可以考虑尝试一下其他的模式。

关于作者

Kevin Jackson 是一名通过敏捷认证的软件开发管理者,具有二十多年的财务服务行为工作经验。在这段时间内,他执行过大多数软件开发实践,包括编码(主要是.net)、分析、测试、支持、项目管理、架构、干系人管理和团队领导。Kevin 乐于与客户共同为复杂问题的制定简单的解决方案,他的用户很喜欢这种做法。他的方法是:专注于目标,减少浪费,一起协作和迭代。即使无效,至少也可以学到些新东西!你可以通过推特与他联系 @softskillpatterns

查看英文原文: Soft Skill Patterns for Software Developers: The “Learning from Unintended Failures” Pattern

2018 年 1 月 03 日 17:391196

评论

发布
暂无评论
发现更多内容

Java 并发基础(一):synchronized 锁同步

看山

Java Java并发 并发编程

智能时代的TCL之舞

脑极体

如何学习数据结构与算法

C语言与CPP编程

数据结构 算法

力扣(LeetCode)刷题,简单+中等题(第26期)

不脱发的程序猿

LeetCode 面试刷题 算法面经 28天挑战 3月日更

SpringBoot + Mybatis + Druid + PageHelper在多数据源下如何配置并实现分页

北游学Java

Java mybatis spring Boot Starter

HTML5+CSS3高级动画的应用实践

云小梦

JavaScript html css3 浏览器API 网页动画

深圳应用区块链提升政务服务效能调查

CECBC区块链专委会

电子发票

用栈、回溯算法设计迷宫程序

不脱发的程序猿

回溯算法 28天写作 3月日更 迷宫程序

Img、net & page新展望:连接感知

云小梦

JavaScript html 网络 用户体验 连接感知

Logstash 中 Ruby filter 使用指南

Langer

ruby Logstash ELK

尤雨溪 Twitch 直播:下一代前端构建工具 ViteJS —— Open Source Friday

清秋

翻译 前端 vite webpack 构建工具

Mac下brew更新及安装Prometheus+Grafana

程序员架构进阶

Prometheus 监控系统 容器化 28天写作 3月日更

区块链列入数字经济重点产业 机构预测其大规模应用将加速

CECBC区块链专委会

数字经济

加密解密之 crypto-js 知识

浩浩子

Redis 作为缓存是如何工作的

escray

redis 极客时间 学习笔记 3月日更 Redis 核心技术与实战

2021春招JAVA面试总结:Java+并发+Spring+MySQL+分布式+Redis+算法+JVM等

云流

Java 编程 程序员 架构 面试

用户体验 | 页面阅读进度提示

云小梦

html css3 用户体验 页面进度提示

ONE MORE

吴小平

使用Flask Nginx Gunicorn和Supervisor部署一个简单的Restful API接口服务器

Langer

Python 部署与维护 服务器部署 web服务

位运算符在 JS 中的妙用

浩浩子

推荐引擎概述

跳蚤

散列(哈希)表算法学习

Nick

数据结构 算法 哈希算法

React 中后台系统多页签实现

清秋

Vue 前端 React keepalive

我对PageRank 算法的理解

跳蚤

shell学习

我是程序员小贱

3月日更

架构师训练营 4 期 第13周

引花眠

架构师训练营 4 期

使用 Typescript 的一些注意事项

浩浩子

浅析Node中间件Koa&Express:原理和实现

云小梦

JavaScript node.js 中间件 koa

浅析 Fabric Peer 节点

Rayjun

第八章作业

LouisN

数据分析作业-用户分析-ReadHub

隋泽

产品经理训练营

软件开发软技能:“从无意识的故障中学习”模式-InfoQ