Code rant: 从硬编码到可配置、规则引擎、低代码DSL的复杂性时钟


当我还是一个年轻的程序员,刚刚开始进入企业软件这个可怕的世界时,一个年长的、更有经验的小伙子对我在软件中硬编码hardcode的做法提出了严厉的警告。"硬编码会在某些时候被改变,你肯定不想为了改变某个增值税税率值而重新编译和部署你的应用程序。”
我把这个建议记在心里,很快我的应用程序需要的每一个值都必须从一个巨大的.ini文件中加载。我仍然认为这是一个很好的建议,但是请注意,就像软件中的大多数事情一样,它在一定程度上是个好建议。超过这一点就会有痛苦。
 
让我向您介绍我的“配置复杂性时钟”:


这个时钟讲述了一个故事。我们从午夜12点开始,有一个简单的新需求,我们迅速将其编码为一个小应用程序。它不会持续很久,只是某个更大的战略计划中的一个权宜之计,所以我们硬编码了所有的应用程序的值。几个月过去了,这个应用程序被广泛使用,但有一个问题,一些业务数值发生了变化,所以我们发现自己需要重写构建和部署,就只是为了改变几个数字。这显然是错误的。解决办法很简单,我们将这些值移出到一个配置文件中,也许是我们App.config中的一些appsettings,现在我们的时间是2点。

时间过去了,我们的应用程序现在在我们的组织中已经有些根深蒂固了。业务继续发展,随着它的发展,更多的值被移到我们的配置文件中。现在appsettings已经不够用了,我们有了值的组和值的层次结构。如果我们做得好的话,现在我们已经把我们的配置移到了一个专门的XML模式中,并被反序列化为一个配置模型。如果我们不是那么好,我们可能已经把重复的和多维的值塞进了一些奇怪的管道分隔的字符串中,现在我们的时间是4或5分钟。

更多的时间过去了,那个令人讨厌的 "首席软件架构师 "被解雇了,我们的小应用程序现在是我们组织的核心。
业务规则变得越来越复杂,我们的配置也是如此。
事实上,在新员工能够成功进行部署之前,有一个相当大的学习曲线。我们的一个新员工是个非常聪明的家伙,他以前就见过这种情况。他说:"我们需要的是一个业务规则引擎"。现在这看起来很有希望。
配置从XML文件转移到数据库中,并有自己专门的GUI。
起初,人们希望非技术性的业务用户能够使用GUI来配置应用程序,但事实证明这是一个错误的希望;将商业规则映射到引擎中需要一定程度的专业知识,而这只有开发团队中的一些成员才拥有。我们现在是在6点钟。
 
令人沮丧的是,仍有一些业务需求无法使用新的规则引擎进行配置。
一些逻辑条件根本无法使用GUI进行配置,因此应用程序必须重新编码并为一些场景重新部署。帮助就在眼前,团队中有人读了Ayende的DSL书。是的,DSL可以让我们写出任意复杂的规则,解决我们所有的问题。团队停止了几个月的工作来实现DSL。当它完成时,这是一个相当大的技术成就,每个人都好好休息了一下。当然,这将意味着任意的硬编码业务逻辑的结束?现在是上午9点的时间。

令人惊讶的是,它还能工作。几个月过去了,核心应用程序不需要任何改变。团队花了大部分时间在新的DSL上写代码。在经历了一些尴尬的事件之后,他们现在在部署任何新的DSL代码之前都会经历一个完整的发布周期。DSL文本文件是受版本控制的,每个版本在部署前都要经过回归测试。调试DSL代码很困难,几乎没有工具支持,他们根本没有资源为他们新的小语言建立一个IDE或ReSharper。随着DSL代码变得越来越复杂,他们也开始怀念能够编写面向对象的软件。团队中的一些人已经开始在业余时间研究单元测试框架。

在下班后的酒吧里,有人打趣说:"我们又回到了四年前开始的地方,硬编码的一切,只是现在用的是更蹩脚的语言。"

他们已经走了一圈,又回到了12点。
 
总结
为什么要讲这个故事?说实话,我从来没有见过一个组织一路走来,但我见过很多人走到5、6或7的时候都感到相当痛苦。我的观点是这样的。

在一定的复杂程度上,硬编码的解决方案可能是最不邪恶的选择。

你已经有了一种通用的编程语言,在你建立一个业务规则引擎或DSL之前,或者即使你的配置通过了一定的复杂程度,考虑到有一个更灵活的构建-测试-部署周期,硬编码可能要简单得多。

当你按顺时针方向走的时候,技术实现会变得越来越复杂。建立一个好的规则引擎是很难的,而编写一个DSL则更难。顺时针每多走一个小时,就会导致软件更加复杂,出现更多的错误,而且新员工的学习曲线也会更加艰难。配置越复杂,在部署前就需要更多的控制和测试。很快你就会发现,在改变一行代码和改变一行配置之间所需的时间长度没有什么区别。你发现你的组织依赖于一种非常罕见的技能,而不是一种常见的技能,比如说编码C#,你发现你的组织依赖于一种非常罕见的技能:理解你的规则引擎或DSL。

我并不是说实施复杂的配置、规则引擎或DSL是不合适的,事实上,如果有合适的要求,我也会抓住机会建立一个DSL,但我是说,在你走这条路之前,你应该理解其中的含义,并认识到你在时间上的位置。