【QCon】精华内容上线92%,全面覆盖“人工智能+”的典型案例!>>> 了解详情
写点什么

区分敏捷开发中未解决的问题:过早出现的和可预见的

  • 2019-11-30
  • 本文字数:7854 字

    阅读完需:约 26 分钟

区分敏捷开发中未解决的问题:过早出现的和可预见的

本文要点:

  • 敏捷的力量在于应对未解决的问题,但同样的力量也会导致技术债务和减少产品价值。

  • 根据本质的不同,未解决的问题应该分为两种:过早出现的、可预见的。

  • 过早出现的问题和可预见的问题,二者之间的区别在于强调重要性还是可能性。

  • 重要的小改变,可以让敏捷开发走得更远。

  • 背后的原则,可以弥合团队心理层面的距离,调和架构师与开发人员的差异。

如今的敏捷出了什么问题?

敏捷开发让人感觉良好,开发人员很喜欢它,但有时候它会出问题。这一点,我们都能感受到,并对其展开争论。


很多敏捷项目随时间推移变得不再成功。比如出现拖延,技术债务不断累积,进度放慢,士气低落。开发人员已经很努力了,但每个冲刺交付的价值却在不断减少。


突然,容易干的活都解决了,剩下的待办任务列表(backlog)中满是庞大、必要、而且对于利益干系人没什么价值的条目。团队与利益干系人开始冲突,项目拖拖拉拉,甚至有可能功亏一篑。


有人说,更严格遵守敏捷的核心原则可以避免此类情况。的确如此。但要想做到敏捷和自我管理,每个人必须可以激励自己,并且严格遵守规则。规则越严格,团队越大,纪律感就越容易削弱。


(关于恢复核心价值观,可深入阅读免费电子书《理解敏捷价值观和原则》。)


还有什么其他办法?

应对潜在原因

没有解决的问题属于待办任务列表。理论上,产品负责人要处理待办任务列表中的所有条目,去除无关紧要的,将最重要的排定优先级,放入冲刺中,直至清空待办任务列表,项目完成。


但在实际情况中并非如此。待办任务列表会一直增长。它收集的工作会与技术债务和无法轻松处理的“烫手山药”一起,进入等待状态。


开发人员认为:待办任务列表相当于泄洪道,让他们有事情可做。敏捷指出:对于你还不了解的东西,或是目前不需要的工作,先放在待办任务列表里,然后暂时置之脑后。有必要时,它会再次出现。很多时候,这么做没问题。敏捷的力量就在与此。


但是到了未解决的问题再次出现时,烫手山药已经变得太烫了,难以处理,技术债务已经变得过于昂贵而无法支付、解决。可用的资源已经根本不足以支持这些工作。


只要在敏捷的做法中加入一些关键的理念,做出一些重要的小改变,就能避免类似问题。


其根源是系统层面的弱点:敏捷方法认为,所有未解决的问题,都是过早出现的问题。没有这么简单,有些问题是可预见的。

过早出现的/可预见的

有两种未解决的问题,它们有本质上的不同。

过早出现的问题

过早出现的问题是当下不需要担心的、未解决的问题。现在去管它们就是浪费时间。可以确定:时机合适,它们自会再度出现。


根据预测,过早出现的问题有四种:


  • 变得无关紧要:比如一个脚本运行起来很慢,但很少运行,几乎无人注意。它是没有解决,但不值得任何人的时间。

  • 保持不变:比如改变窗口大小就会持续闪动的 UI 界面,确实不好看,但不会阻碍关键流程,而且不管什么时候解决它,整体的工作量没有变化。

  • 变得容易:比如复杂的配置 UI 界面。等到后来,你会更深入了解项目,而且更好地理解数据模型。你有更多可重用的 UI 组件,是因为其它原因加进来的。难度就会随项目发展降低,再稳定在某个最低程度上。当你让它在待办任务列表中慢慢变成熟时,这就是你所期望的结果。

  • 消失不见:比如某个复杂的功能特性需求,再过六个月,都无法确定用户是否还需要。不需任何工作就能解决此类问题。



当环境发生变化,或是过早出现的问题发生变化时,它就会再次出现。将某个问题视为过早出现,有点赌博的意味。对于很多问题来说,先放在一边是正确选择。

可预见的问题

可预见的问题是会随时间推移变得更难处理的问题。技术债务和烫手山药就属于它们。敏捷会产生很多此类问题。


越早处理此类问题越好。如果你总是无视它们,总有一天,可预见的问题会变得超过“可行性边界”(feasibility limit),过于庞大而无法解决。


经验表明,在一定时间内,难度会缓慢增加,当环境发生变化,问题难度就会陡增。



可预见的问题有两种:


  • 直接:问题本身就是造成问题的根本原因。比如执行缓慢的门户初始化脚本,在不断增长的数据库中费力地运行。

  • 间接:根本原因不在于造成问题本身,而是导致其它地方问题恶化。比如侵入式框架(intrusive framework),依赖它的代码越多,它导致的冲突越多,以后就难以去掉这个框架。


要想判断一个问题是否可预见,需要一些预测技巧:


  • 对于表面原因和根本原因的感觉

  • 理解为什么某些模式会以后导致特定问题

  • 考虑各种场景,估算它们的概率

  • 预测哪些环境有可能变化,以及何时变化

  • 在问题达到可行性边界之前,你还有多少时间


这些技巧一般要靠架构师具备,而且需要跟每天疯狂写代码的工作有点儿距离。


可预见的问题有一个或是多个 可预见的场景。你可以预测到:如果不行动,情形会如何恶化。你可以预测到:当特定条件变化时,它如何跨越可行性边界。


会随时间恶化的问题总是可预见的,正是由此得名。


架构师的职责,就是要判断哪些问题是过早出现的,哪些是可预见的。

敏捷中可预见的问题

敏捷的本质缺点在于:所有未解决的问题都被视为过早出现的问题,不存在可预见的问题。


开发人员相信:当工作不那么繁忙的时候,他们可以处理待办任务列表中的工作。同时,可预见的问题确实存在。既然隐藏在待办任务列表中,工作任务如何改变是不可见的。有些保持不变,有些变得简单,或者不再重要,或者消失不见。但是有些会变得更糟糕。


在最不恰当的时候,问题的困难程度会突破可行性边界。这些问题从坟墓中成群用处,变得太大而无法解决,让所有人都感到恐惧。


资金充足的组织,可以用钱解决此类问题。上更多服务器,找来更多开发者。可行性边界会再次提高,此后再次发作。没有太多资金的组织,项目就会失去创新能力,逐渐过时,最终死去。

架构师应该为敏捷团队做的事

除了日常职责之外,架构师应该时常浏览待办任务列表,并将问题标识为过早出现的和可预见的;同时说明可预见的场景、可能的情境变化风险和可能造成的影响。


架构师应该选择最迫切的、可预见的待办任务列表条目,并产出可以解决它们的设计。然后将这些设计扼要转化为日常工作任务。设计应该足够成熟,可以切分难点,变为能够直接动手开工的任务。


设计是有必要的。把一个老问题直接丢回给程序员,又没有任何计划,这会造成压力、阻力和抵抗。问题在待办任务列表中出现是有原因的,可以预见也是有原因的。


为可预见的问题排定优先级很复杂,涉及到多个层面,包括:价值、工作量、破坏程度、达到可行性边界的概率和剩余时间。这些参数很难量化,最好由经验出发加以评定。


一条根本原则是:最“间接”的可预见的问题应该首先解决,因为它们造成的麻烦最严重,而且到处都是,先解决它们可以释放出资源,以解决优先级稍低的问题。

敏捷角色中的信任

程序员应该相信:那些一旦忽略就会变得恶劣的未解决问题,有架构师可以搞定它们。有必要时,这个架构师可以让问题再次出现在程序员的日程中,而且提供了相应设计,并且规划了优先级,所以不会产生压力。


唯一要注意的是,程序员不能过度依赖这个“服务“,不能把待办任务列表当成垃圾堆,把所有尚未认真想清楚的问题都丢进去。

补偿产品负责人?

处理过早出现的问题和可预见的问题,这是不是意味着架构师在做一个糟糕的产品负责人的工作?并非如此。这种方法将待办任务列表变得清晰、可行,产品负责人可以利用其中的信息规划冲刺迭代。


一般来说,产品负责人无法承担识别这两种问题的责任,他们缺少相应的深入技术知识,而且存在利益冲突:在压力时刻,保证客户和团队开心,这是头等大事。产品负责人不应该去刨根问底翻旧账。

所以,敏捷可以成功

用上述方法,敏捷就能像期望的那样成功:成为可持续的开发方法,让开发人员的生活可以承受得了,同时不必积累技术债务和烫手山药。


必须付出的小代价是要认识到:只有开发人员,没有人能承担架构师的角色,这样的自管理团队不可持续。


(深入阅读:《也许敏捷才是问题》《敏捷宣言:软件架构师角度的思考》

还有更多东西要考虑

这个过早出现的和可预见的问题分类原则还涉及更多主题。


总而言之,该方法是要具体化一种没有说出的张力——短期内写代码并带来成效,长期内的可维护性和可演化性,这两者之间的张力。


(深入阅读:《构建可演化的架构》一书,特别是第 2、6、8 三章。)

开发人员的心态

所有架构师都知道:开发人员从完全不一样的角度对待项目,这会导致很多误解。使用过早出现的和可预见的原则,有助于弥合差距。

待办任务列表作为分散注意力的垃圾堆

程序员是手艺人,手上忙着各种事情。他们希望干扰越少越好,从而保持“心流”状态。程序员善于将干扰从自己的思维空间中赶出去。


为了让代码工作,他们有无数的问题要解决,再加上一大堆问题,只会造成压力。对于偶然出现的问题,程序员需要待办任务列表将它们置于一边。


架构师有时候会认为这是置问题于不顾。但程序员觉得这么做没问题。此时缺少的是整体掌控,需要有人可以过滤可预见的问题,在以后的开发周期中再放进来。


这样的待办任务列表掌控不应是程序员的职责,他们还有日常开发工作。否则就会导致利益冲突和压力。此类压力会触发绝望,使得大家想从头重新开发,拉低整个团队士气。

不去评判未解决的问题

程序员相信自己的编码能力,相信自己可以解决遇到的任何问题。这是他们的日常工作。


在程序员看来,架构师的设计让他们感到被评判,似乎他们的编码能力不足,然而正是他们在做所有的实际工作。但是架构师的意图是中性的:有了好设计,程序员的编码能力可以走得更远。


在日常对话中引入过早出现的和可预见的问题分类原则,可以让此类讨论免去评判的意味。这与谁工作效率低下、能力不足、谁更好或是更聪明无关。关键是要判断会发生什么问题,从而加以预防。


当待办任务列表中加入新条目时,如果程序员可以针对可疑的可预见的问题附加警告标签,并添加可预见的场景,对大家都有帮助。

将框架作为可预见的问题

程序员会不小心将框架放到代码库中。他们遇到某个问题,而且发现很难解决,那就看看还有谁也碰到类似情况。找到一个框架,没问题,看上去挺好,不错,继续写代码。


架构师认为,框架是结构性的决策。程序员认为,框架是可以直接用的代码。如果程序员每次选择框架的时候,都要暂停冲刺迭代,保证选择过程深入彻底,那他们什么都做不了。


对于架构师而言,匆忙决策会减少可选项。常常出现的是,过早选择框架会在后面让人失望。一旦用上框架,再想去掉就很麻烦。如果过了很久再说,那就完全不可逆了。


这是可预见的问题。框架选择应该在冲刺之前完成。提前选择框架,应视为可预见的问题的解决方案。

减少对英雄的依赖

敏捷希望程序员在面对难题时充满勇气:团队是自治的,可以相信的,知道该做什么。实际上并非总是如此。一般来说,随着项目进展,利益干系人的信任会不断销蚀,当交付的产品成功时才能恢复。


信任销蚀得越多,团队要想说服郁闷的利益干系人,让团队开发“模糊”的任务,则需要的勇气越多。慢慢地,对于很多人而言,就不再具有这样的勇气。他们不愿意提出难以解释的讨论主题。逃避会让可预见的问题向可行性边界慢慢前行。


和利益干系人讨论时,说明某个问题是可预见的,可以去掉勇气的因素,让其成为更中性的议题。能做到以下几点就更容易:


  • 需求明确

  • 不做评判

  • 结果有益

  • 每个人都能理解置之不理的后果


(深入阅读:《敏捷组织中沟通勇气的重要性》

开发人员抵触设计

架构师希望程序员理解、支持自己的设计,并且能和自己辩论,发现其中的潜在问题,同时积极提出想法,加以改进。有价值的设计应该经得起健康的辩论。过早出现的和可预见的问题分类原则能够让辩论更有价值。

抵触复杂性

如果程序员认为某个设计太过复杂,他们就会抵触,同时相信更简单的解决方案更好。如果存在更简单的解决方案,那当然再好不过,赶紧用起来!可惜,复杂方案常常是有必要的,因为简单方案力有不逮。


程序员可能公开抵触,抑或消极听命,拒绝主动贡献想法。这是有成本的,会带来更多潜在设计问题、写代码时更多返工、将来更多要修复的 bug。


通常,架构师的理由是考虑未来远景和设计原则。但如果去跟程序员解释这些整体图景,那就过于庞大而有些杂乱了。越想讲清楚,程序员越是固执己见。


抵触设计常常伴随这些情绪:失望、畏惧、感到别人更聪明、或是被夺去自主权。光靠讨论很难克服,因为程序员总是用技术论点讨论技术和情绪话题。此种讨论难以捉摸,而且无人受益。架构师应该一起来预防对设计的抵触。

抵触设计中的潜在问题

设计的问题会增加抵触心理。人会犯错误。架构师也不例外。设计越复杂,架构师就越有可能漏掉某些东西,留下设计问题。


程序员认为:遇到潜在问题,就得丢弃刚刚写好的代码,还增加了复杂性,错过截止日期。设计潜在问题常常难以察觉,而且很不容易解决。程序员从经验出发,担心越是复杂的设计,越容易遇到潜在设计问题。

为什么展示整体图景没有作用

程序员说:“我看不清大局”,或者“你把这个弄得太漂亮了”,他们是在说:如果他们找不到一个做得这么复杂的合理的理由,那就不存在合理的理由。


用展示整体图景来说明你的选择,这么做很诱人,但是会有反效果。


程序员认为:雄心勃勃的设计,就好比架构师用大油漆滚筒画出了西斯廷天顶画的轮廓,留下无数细节,等着程序员去完成。



左:架构师如何看待设计 右:程序员担心自己要做的事情


展示整体图景会让程序员崩溃,因为他们看到有那么多东西,就会想:“天啊,我怎么做的了这个。”庞大的设计常常是人脑海中难以容纳的,还得担心存在潜在设计问题。


如果你的庞大设计包括未知因素,那就更糟糕了。存在不了解的东西,在架构师设计过程中很正常。在程序员心中,想到糟糕的客户需求以及自己要为之写出的代码,自然会将未知因素转换为不确定性、更多要解决的问题、更担心潜在问题,从而错误认为:架构师不知道自己在干什么。


如果程序员想了解大局,那就要找到根本原因。最好只跟有经验的人分享庞大的整体设计,他们不会晕头转向。

为什么抽象的讨论没有意义

架构师认为,使用某些抽象是必要的,由此就不用在各步骤之间想得太多。当然,这是架构师的必要能力。而且很容易就会向程序员解释这些设计抽象。


程序员认为原则和设计模式就像汽车:它们都能把你带到目的地,那为什么要花钱去买最贵的呢?


与程序员讨论抽象设计,会将讨论带到品味上,或是争论谁能产生“最干净”的抽象。两者都不会带来进展。

为什么业务原因不起作用

架构师认为:某些技术决定是出于商业原因。比如,能让商业合作伙伴更轻松的技术能力,会让组织成为市场领导者,而不是偏安一隅。架构师应该参与此类决策讨论。


业务原因不能说服程序员。相反,他们会把讨论转向商业人士的不理性头脑、产生的无理需求,从而否决架构师设计的合理性。


程序员畏惧商业元素。过去与合作伙伴的经验,常常导致程序员要去收拾烂摊子,由合作伙伴要求无度而且能力不足的 IT 人员留下的烂摊子。程序员认为:商业上的成功,意味着由一意孤行的管理者提出不切实际的要求,导致自己被迫做出让人后悔的应急方案,而且总被事故推着走。


很多程序员不喜欢商业、销售和市场,无论是人还是相关主题。三十年的呆伯特漫画可以告诉你为什么

那么,到底该怎么做才有用?

从整体设计产生可执行的设计方案,避免论及宏大的、深奥的、未知的因素,这是架构师的职责所在。


设计应该从一系列你想解决的、可预见的问题和场景开始。这样一来,架构师就能说明为什么简单方案不行,或是无法坚持足够长时间,因此不能使用。


最好能写下多个可预见的场景,让它们彼此关联。为什么?可预见的场景可以将抽象和相关问题转变为具体例子。如果只有一个场景,程序员可能会倾向于找到某种简单的权宜之计,只解决这个场景的问题,而不是它代表的整个问题类别,从而低估问题范围。


有些相关问题很明显,但是过早出现,那就要归类为“不在范围之内”。


列出过早出现的和可预见的问题的设计列表,可以让程序员知道不要担心什么, 应该重点关注什么。这让架构师在抵触到达沸点之前,可以中性地展开设计辩论。


对于程序员来说,由过早出现和可预见驱动的设计更容易理解,更易于寻找潜在设计问题,更便于找到改进方案。这么做的同时,还不会产生复杂设计带来的更多压力、不确定性和过多问题导致的负担。


这还能为程序员留出更多空间,让他们可以自己决定,同时保证他们深入了解架构师的意图,不需要过多详细需求,还能让设计切合当前冲刺。


(观看关于设计抵触、前期大型设计、冲刺中交付架构的视频。)

整而合之

还得聊聊可能性

现在,讨论从争辩一个问题的相关性转移到了可能性。它有多大机会发生?情境改变的可能性有多少?还需要多久,它会达到可行性边界?


为了简化,可预见的问题的可能性可以分为三个级别:


  • 不可避免:必将发生,必须行动

  • 火灾风险:难以发生,一旦发生,必须有类似自动喷水设备的措施

  • 流星撞地球的风险:几乎不可能发生,而且不值得去修个太空盾牌那样的防范措施

必要的同理心

你必须理解程序员的心态,看到在新的做事逻辑生效之前,为什么会发生某些事情。突然就将问题标识为“可预见的”,会让人觉得过于生硬,让人觉得在评判程序员的脑子、工作素质或编码能力。开发人员也许会将未解决的问题作为秘密保守,自己承担相应负担,就像程序员们一直以来自然而言的做法。你会用一个问题取代另一个问题。


(深入阅读:《同理心是一种技术能力》。)


如果程序员觉得说出想法不是问题,讨论未解决的问题就不会觉得自己能力不行、懒惰或是不上进。未解决的问题是努力工作和良好意图的中性后果,搞定麻烦的那些问题正是架构师的职责。这样敏捷就能起作用了。


(深入阅读:给架构师的提示,包括“垂直分片”建议,可以帮助大家概要了解可预见的问题,同时不会让架构师听上去像个末日预言家。)

那么关于留出更多选择呢?

留出更多选择,这是架构师的工作:你无法预言,所以你要让自己有更多选择。听上去似乎与过早出现的和可预见的问题分类原则直接冲突。


实际上,留出更多选择是积极地安排你的未解决问题,这样你就可以将尽可能多的问题变成过早出现的问题,只剩下尽可能少的可预见的问题。


架构师能做的最有用的事情,就是解决一个可预见的问题,该问题可以将很多其他可预见的问题变为过早出现的问题,让你有更多选择。

结论

引入过早出现的问题和可预见的问题之间的区别,是一种全新的思考和讨论习惯,它可以:


  • 让烫手山药和技术债务变得可以讨论,同时不会让程序员因为压力而崩溃

  • 让架构师承担职责,应对那些因为没人管就会失控的问题

  • 有助于以中性的方式讨论设计,每个人都可以理解

  • 针对更加复杂但有必要的解决方案,让大家都能更好地了解

  • 预防潜在设计问题和 bug


区分过早出现的和可预见的问题,可以让敏捷成为本应成为的可持续的开发方法。

作者介绍

Bas Groot 住在阿姆斯特丹,已经创业 19 年。他开发了 web 应用平台和内容管理系统,那时还是 1996 年,类似软件尚无名称。接下来的 7 年,他为某金融服务软件供应商工作,担任使用同一平台的 web 解决方案部门的架构师。在其职业生涯中,Bas 主管并培训开发人员团队,作为架构师领导产品开发和软件设计。他实施落地了众多客户项目,包括政府、金融、法律、非盈利、汽车和广播媒体等多个行业,用到多种敏捷方法。Bas 主导开发的有:两种编程语言、一种图形化底层代码应用编程环境、一种具备实时集群编码能力的 web 应用框架、一个数据建模框架、一个软件和内容分发部署框架、一个 web 开发代码库等等。Bas 有实际上手开发和使用框架的经验,同时对于设计软件和方法论有深厚的抽象能力。


“出现问题时,我要做的第一件事,是看人们遵循什么规则,以及背后的原因。”


原文链接


Categorise Unsolved Problems in Agile Development: Premature & Foreseeable


2019-11-30 08:002546

评论

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

现在后端开发都在用什么数据库存储数据?

Linux服务器开发

MySQL 数据库 后端 中间件 Linux服务器开发

架构实战营 模块六:课后作业

Ahu

架构实战营

三星T5 格式化成APFS

SamGo

学习 硬件产品

🌏【架构师指南】分布式事务(XA)与一致性算法(Paxos、Raft、Zab、NWR)

洛神灬殇

ZAB raft协议 paxos协议 6月日更

如何应对不好回应的沟通场景?

石云升

读书笔记 沟通 6月日更

网络攻防学习笔记 Day49

穿过生命散发芬芳

网络攻防 6月日更

作为程序员,你会使用Notion吗?

Bob

程序员 Notion 笔记

经济日报刊评:数字人民币应用场景拓展

CECBC

金融科技加速经济低碳转型 但面临政策、市场、技术等多方挑战

CECBC

双非渣本后端,三个月逆袭字节,入职那天“泪目”了

Java架构师迁哥

Angular | 浅谈Angular错误处理方式

devpoint

angular.js angular 6月日更

模块6 学习总结

TH

冯 · 诺依曼结构原理及层次结构分析

若尘

计算机组成原理

前端 JavaScript 之『节流』的简单代码实现

编程三昧

JavaScript 大前端 js 防抖节流 代码实现

窥见AI工业化开发黎明:华为云如何将AI进行到底

脑极体

Dajngo网站开发---Task2

IT蜗壳-Tango

6月日更

模块6作业 拆分电商系统为微服务

TH

架构实战营

太为难我了,阿里面试了7轮(5年经验,拿下P7岗offer)

Java 程序员 架构 面试

排序算法之冒泡排序

xcbeyond

排序算法 冒泡排序 6月日更

「SQL数据分析系列」8. 分组和聚合

数据与智能

数据库 sql 大数据 存储 计算

堆与堆排序

wzh

Java 数据结构 算法 堆排序 数据结构与算法

手写一个简单的SpringBoot Starter

赵镇

Redis入门一:简介

打工人!

数据库 nosql redis 6月日更

模块6课后作业

方堃

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Java 程序员 架构 面试

拆分电商系统为微服务

唐江

架构实战营

setTimeout(〒︿〒) 请原谅我一直以来对你的忽视

编程三昧

JavaScript 大前端 定时器 基础知识

韩信大招:一致性哈希

悟空聊架构

分布式 一致性hash 6月日更 hash算法

Three.js杂记(十二)—— VR全景效果制作·中

空城机

大前端 three.js 6月日更

架构实战营 模块六作业

netspecial

架构实战营

电商系统微服务拆分设计

Lane

区分敏捷开发中未解决的问题:过早出现的和可预见的_研发效能_Bas Groot_InfoQ精选文章