代码随想

2012年5月7日 星期一

配置复杂度的时钟

当我还是个年轻的程序员,刚开始进入令人恐惧的企业软件世界时,一位年长的、经验丰富的同事严厉地警告我,不要在软件中硬编码值。“它们_总有一天_会需要修改,而你肯定不想仅仅为了修改增值税税率就重新编译和部署你的应用程序。”我把这个建议牢记在心,很快我的应用程序需要的每一个值都从一个巨大的.ini文件中加载。我仍然认为这是个好建议,但请注意,就像软件中的大多数事情一样,这个建议_在某种程度上_是好的。超过那个点,痛苦就开始了。

让我向你介绍我的“配置复杂度时钟”。

ConfigurationComplexityClock

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

时间流逝,我们的应用程序现在在我们的组织中变得根深蒂固。业务不断发展,随着业务的发展,越来越多的值被移动到我们的配置文件中。现在appsettings不再足够,我们有值组和值层次结构。如果我们做得好,到目前为止,我们将把我们的配置移到一个专用的XML模式中,该模式被反序列化为一个配置模型。如果我们做得不太好,我们可能已经将重复的和多维的值硬塞到一些奇怪的波浪号和管道分隔的字符串中。现在我们在时钟上的4点或5点。

更多的时间过去了,烦人的“首席软件架构师”被解雇了,我们的小应用程序现在是我们组织的核心。业务规则变得更加复杂,我们的配置也是如此。事实上,现在新员工需要相当长的学习曲线才能成功地执行部署。我们的一位新员工非常聪明,他以前见过这种情况。“我们需要的是一个业务规则引擎”,他宣称。现在这看起来很有希望。配置从它的XML文件移动到一个数据库,并且有它自己的专用GUI。最初,人们希望非技术业务用户能够使用GUI来配置应用程序,但这最终变成了一个虚假的希望;将业务规则映射到引擎需要一定水平的专业知识,只有一些开发团队成员才具备。我们现在在时钟上的6点。

令人沮丧的是,仍然有一些业务需求无法使用新的规则引擎进行配置。一些逻辑条件根本无法使用其GUI进行配置,因此必须为某些场景重新编码和重新部署应用程序。帮助就在眼前,团队中的某个人阅读了Ayende的DSLs书。是的,一个DSL将允许我们编写任意复杂的规则并解决我们所有的问题。团队停止工作几个月来实现DSL。当它完成时,这是一项相当大的技术成就,每个人都好好地休息一下。当然,这将意味着任意硬编码业务逻辑的终结?现在是时钟上的9点。

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

下班后在酒吧里,有人打趣道:“我们又回到了四年前的起点,硬编码一切,只是现在用一种更糟糕的语言。”

他们已经绕了一圈,回到了12点。

为什么要讲这个故事?说实话,我从来没有见过一个组织完全绕完一圈,但我见过很多已经到了5点、6点或7点,并且感到相当痛苦的组织。我的观点是:

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

你已经有了一种通用的编程语言,在你开始构建业务规则引擎或DSL的道路之前,或者即使你的配置超过了一定的复杂程度,也要考虑到,通过更流畅的构建-测试-部署周期,仅仅硬编码它可能会简单得多。

当你顺时针绕着时钟走时,技术实现变得越来越复杂。构建一个好的规则引擎是困难的,编写一个DSL则更困难。你顺时针方向每多走一个小时,就会导致更复杂的软件,更多的bug,以及新员工更难的学习曲线。配置越复杂,部署前需要的控制和测试就越多。很快你就会发现,更改一行代码和更改一行配置所花费的时间几乎没有区别。你的组织依赖于一种非常稀有的技能:理解你的规则引擎或DSL,而不是一种常见的技能,比如编写C#代码。

我并不是说实现复杂的配置、规则引擎或DSL永远是不合适的,事实上,如果我有合适的需求,我会立即抓住构建DSL的机会,但我_是_说,你应该理解这些含义,并在你走这条路之前,认识到你在时钟上的位置。