迭代无法解决的架构问题

当一个问题无解、或左右为难的时候,

就不能只从当前状态出发解决问题了,

而是得跳出当前状态,思考导致这种困境的原因。


就像走迷宫一样,遇到死胡同之后,要退回来。


有些软件问题,其实也是类似的,

怎么看都无解,就得好好想想了,这种状态是如何造成的?


比如,我们依赖了一个不稳定的外部系统,它频繁失败,

用户感知到的都是我们系统的问题,怎么办?

我们又没办法避免这个外部系统失败。


又比如,一个复杂的系统,里面包含很多业务逻辑,

用户经常需要确认具体逻辑后才能放心使用,

怎样减少咨询量?总不能让用户自己看代码吧。


这些都是很棘手的问题。

难解之处在于,几乎所有解决方案都堵死了。

似乎保持现状才是最好的办法。


下面我们就来分析一下这两个问题,看看怎样退一步海阔天空。

功利主义

在分析问题之前,需要明确的是,退回来及时止损需要头脑清晰

并不是每个必须支付的成本,都得有回报。


我认为仅仅通过 “投入产出比” 来制定所有决策,是不恰当的,

有些事情,并不是那么好衡量的,

也有一些事情,很长时间之后才能看到价值。


所以,如果总是抱着 “功利” 的心态去做软件,

那其实就不应该做软件,

因为肯定还有比做软件 “投入产出比” 更高的事情嘛。


走错了路,就得有勇气退回来,

还想着只往高处走,这就是出现悖论的原因。

依赖的脆弱性

我们依赖了一个不稳定的外部系统,它频繁失败,

用户感知到的都是我们系统的问题,怎么办?


依赖链路越长,整个系统的稳定性就越差,

这是因为每个环节的失败,都会导致整条链路不可用。


所以,总是把系统按调用链路进行拆分,并不是一个好主意。

职责分离的同时,也增加了系统出现问题的可能性。


此外,对于链式系统中的每个环节来说,上下游管理都是一笔不小的开销,

依赖一个脆弱的系统,却还得提供可靠的功能,

产生这个悖论的原因就出在这里。


链式结构的系统,整体可用性不可能高于每个环节的可用性。


因此,要么提高每个环节的稳定性,

要么别对外声称整个系统是可靠的,及时向上抛出异常。


用户操作一个貌似可靠的系统,最后得到一个*常失败的结果,

这才是导致心态崩溃的根本原因。


最后,在调用外部服务的时候,不应当只关注正常的返回结果,

而是应当把每个外部服务,理解为多返回值(一个正常 + N种失败)接口。

对于每种可能的失败,都应该专门处理。

延迟决定

一个复杂的系统,里面包含很多业务逻辑,

用户经常需要确认具体逻辑后才能放心使用,

怎样减少咨询量?


Clean Architecture》中有过这样一段话,

Good architects design the policy so that decisions about the details can be delayed and deferred for as long as possible.


好的软件架构总想办法延迟决定

这是因为,越早做出的决定,就会包含越多的假设。

软件设计是很惧怕假设的,假设意味着,未来它可能是正确的,也可能是不正确的。


过度设计的原因之一就是,假想敌太强大。


回到减少咨询量的问题上来,用户之所以不放心使用,

是因为我们提前帮用户做了很多不必要的事情。

更合理的方法是,延迟决定,让用户来决定需要做哪些事情。


用户想做一些事情,反而跑过来问我们给他做了没有,

这种 “保姆式” 的软件系统帮了倒忙。


业务逻辑本来应当是当前系统该隐藏的那部分知识,这正是软件边界如此划分的原因之一,

如果边界两侧,总是会有不可避免的信息沟通,

这只能说明是边界的划分不合理。


我们应该退回来解决边界划分问题,而不是信息沟通问题。

结语

以上我们分析了软件系统中的两个典型问题,

(1)依赖了一个频繁失败的外部系统,怎么服务好上游用户?

(2)怎样避免用户咨询复杂系统的业务逻辑?


可以看到这两个问题,都跟软件架构有关。

这并不是一个巧合。


软件开发过程中的很多 “死胡同” 其实正是架构问题的外在表现。

由于架构不合理,所以怎么做都不对。


近些年来在国内疯狂流行的 “敏捷” 开发,是重迭代轻架构的,

给人一种假象,任何问题都可以通过迭代抹平,

其实不然。


迭代到 “死胡同”,还不肯及时止损的开发团队,非常的痛苦。

一方面背着业绩压力,不得不往高处走,

另一方面是,实在走不上去,又不能往回退。


不知道这样的局面,什么时候才能有所改善。