Skip to content

1.7 用否定之否定来看待技术选择

否定之否定起源于古老的朴素唯物辩证法,被黑格尔首次系统性的阐述 [13]。我们不使用哲学相关的"大词",用通俗的话来说就是通过不断看到问题的反面并来回倒腾加深对事物的看法。

其实敏捷就是否定之否定的结果。举个例子来说,没有任何管理的软件开发是无序的,有任何变化都会被工程师立即响应。大家认识到这种开发模式的局限性后,提出需要约束开发过程,像"瀑布"一样经历分析、设计、开发、测试等多个阶段,让软件开发具有被工程管理的可能性。

这就是第一次否定。

但是随着大家对瀑布开发的进一步认识,逐渐发现其存在实践上的局限性。瀑布开发的局限性是反馈周期太晚,过于依赖原始设计的可靠性。但是实际上,由于人们很难从一开始就做出完美的详细设计,并且也不满足响应变化的要求。

所以人们对瀑布进行了否定,产生了敏捷的思想,这就是否定的否定。可以说敏捷就是克制的在迭代内装了一个瀑布。

那么,敏捷的局限性是什么呢?这值得我们思考,这并非是在否定敏捷的价值。实际上,任何具体的方法论都有其局限性,没有局限性的事物会变成形而上学。

我们否定掉又重新肯定的东西很多,这些东西还经常影响技术选型和对问题的判断。拿非常火的领域驱动设计(DDD)来说,它的局限性是什么?怎么通过否定之否定来分析呢?

领域驱动设计中的充血模型是对 Smart UI 和事务脚本的否定,由于直接操作数据库往往会忘记考虑业务一致性约束,非常经典的例子就是订单的总价需要订单项来整理计算出来,单独修改任何一项的数据都会带来业务一致性问题。

领域驱动中使用聚合来处理一致性问题,在很多人理解的领域驱动设计中,是通过将业务逻辑"充血"到实体(聚合根+实体)中。并对聚合"整存整取"的内存操作来完成业务一致性封装的。领域驱动设计的局限性也在于此——充满理想化的思想让我们掉入下一个更美好的陷阱,"充血 + 整存整取"总是会带来各种各样的问题,因为现实世界没有一个足够大且永不断电的内存。

理想化的方案总是和局限性共存。它俩就像黑白两面,让工程师被梦魇笼罩。被否定之否定的规律支配的事物还很多,当我们得到了一个方案的好处,常常需要在看不见的地方付出代价。中台是另外一个经典例子,中台建设获得了可复用的支撑能力,但是它的局限性是失去了灵活性,因为中台一旦被使用,改起来影响就大了。

那么我们怎么通过否定之否定来对新事物进行分析呢?

否定之否定规律能发挥作用因为事物矛盾的存在,正是因为旧的矛盾就会出现新的方案,当新的方案出现后解决了原来的矛盾,新的矛盾又会出现。分析新的技术或做出选择时,可以从下面几个方面着手:

  • 该技术解决旧的矛盾是什么,旧的矛盾是否是我们需要解决的主要矛盾? 该技术解决旧的矛盾是什么,旧的矛盾是否是我们需要解决的主要矛盾?

  • 使用该技术是否会带来新的矛盾,或者是否会将原来的次要矛盾变成新的主要矛盾? 使用该技术是否会带来新的矛盾,或者是否会将原来的次要矛盾变成新的主要矛盾?

  • 那些矛盾是我们能接受或者容忍的? 那些矛盾是我们能接受或者容忍的?

从这三个方面应该能帮助我们做出一些技术、软件工程上的选择。

  • 如果引进一个新的技术或者实践,但是它解决的旧矛盾并不存在或者不是主要矛盾,那么这项选择可能不太合适。 如果引进一个新的技术或者实践,但是它解决的旧矛盾并不存在或者不是主要矛盾,那么这项选择可能不太合适。

  • 如果引进一个新的技术或者实践,但是它带来的新矛盾我们无法接受,那么也不合适。 如果引进一个新的技术或者实践,但是它带来的新矛盾我们无法接受,那么也不合适。

以一个真实项目案例来说,某项目是 Node.js 作为主要的开发语言,但是出现了很多 Bug,不够稳定。新的技术领导认为,这是 Node.js 语言的原因,需要换成 Java。结果花费巨大的代价,但效果并不好,实际上是因为软件不够稳定而不是 Node.js 语言的原因,那么这次的"否定"无效。

而有效的否定之后否定就像瀑布否定无序开发,敏捷否定瀑布;网站开发中,模板开发否定了对 CGI 输出字符串的模式,前后端分离否定了模板开发,而后端渲染(SSR)又部分否定了前后端分离。

那么,我们每前进一次,为新技术而满怀激动的时候,带来的新矛盾是什么呢?

Released under the MIT License.