业务银弹:配置化

92 阅读5分钟

初步认识

在开发中我们可能经常会遇到诸如:配置化、DSL、规则平台、Schema化协议这样的字眼。

你是否隐隐约约感觉他们有一些相似的地方 ?

  1. 他们是比代码更加轻量化的描述方式
  2. 他们通过 “配置”的方式描述一个业务方案
  3. 当我们谈及“配置化”,说明这部分内容放在代码中不够灵活/泛用

举例

小明是一个程序员,他发现代码中很多地方需要用到一个“文章类型”元数据 ,而当前的类型分布在各个死代码处,他想,能不能把它做成配置统一管理。于是他写出了下面这样的配置。

[
    {
        type: '',
    }
]

这是常见的配置化方案,这样子做好像没有什么问题,而且非常方便,只需要维护一个配置,各处业务逻辑就能同步修改。

让我们升级一下需求,进一步研究配置化的哲学

  • 每一个类型具有不同的展示方式
  • 在 51 期间,生活文章类型的进行 20 % 的推流 , AGC 类文章进行 10 % 的推流
  • 五一专题文章只在五一期间存在,过期后回到普通文章类型

小明加班写出了以下两份配置

[
    {
        type: '生活文章',
        extra:{
            condition: "days.between("2025-5-1","2025-5-7")",
            action: "push",
            threshold: "0.2"
        }
    },
     {
        type: 'AGC 文章',
        extra:{
            condition: "days.between("2025-5-1","2025-5-7")",
            action: "push",
            threshold: "0.1"
        }
    },
      {
        type: '五一专题文章',
        extra:{
            condition: "!days.between("2025-5-1","2025-5-7")",
            action: "hidden",
        }
    },
    {
        type: '普通文章',
        extra:{
            condition: "!days.between("2025-5-1","2025-5-7") and type === '五一生活文章'",
            action: "show",
        }
    },
]
[    {        type: '生活文章',        extra:{            hidden:false,            push:{                threshold: "20%",                period: ["2025-5-1","2025-5-7"]
            }
        }
    },
  {
        type: 'AGC类文章',
        extra:{
            hidden:false,
            push:{
                threshold: "10%",
                period: ["2025-5-1","2025-5-7"]
            }
        }
    },
  {
        type: '五一专题文章',
        extra:{
            hidden:{
                period: ["2025-5-1","2025-5-7"],
                fallback: "普通文章"
            },
        }
    },
]

你觉得哪一份好 ?

公布答案,第二份更好。 为什么呢? 有的同学可能认为第一份更佳,因为他更工整

其实不然,第一份配置其实耦合了一些“逻辑”,而不是“纯数据”。不信?让我们把上面的逻辑部分用代码写出来看看:

switch(type){
     case : "生活文章"
         if(days.between("2025-5-1","2025-5-7")){
             push(type,0.2)
         }
         return
     case : "AGC文章"
         if(days.between("2025-5-1","2025-5-7")){
             push(type,0.1)
         }
         return
     case : "五一专题文章" 
         if(!days.between("2025-5-1","2025-5-7")){
             type = "普通文章"
         }
         return
}

这样看,你是不是觉得代码会比第一份更好。如果还不明显,你可以尝试给小明多加几个需求,由于配置一不是纯元数据的,包含了操作逻辑,所以他会随着业务增长而变得臃肿。同时由于他只是一个配置,没有代码与生俱来的优势:图灵完备性 + 可维护性 + 可拓展性。所以这时候我们不如直接用代码写。

但是你或许注意到,代码中有很多写死的数据,如果要更改这部分,我们还得改代码,效率很低。所以这一部分才是我们需要提出到配置的。总的来说,我们将一个业务逻辑分工 :

  • 配置,负责保存参数。
  • 代码,负责使用参数执行业务逻辑。

换句话说, 配置用来说 what,而代码表达用 “what”去干什么,也就是how。至于 why,通常只有产品同学知道😃。

对比思考

让我们来对比一下配置和代码

我们已经得出结论:在一个业务中,代码负责逻辑行为描述,配置负责参数变量的描述,相互合作构成一个合理的架构。

优点

  1. 没有复杂的代码逻辑,非技术人员也能很快看懂并操作
  2. 聚合分散的数据,一处配置多处同步,提高业务迭代的效率

缺点

配置的缺点,其实相对的就是代码的优势

  1. 可维护 : 由于没有工具链的支持,在配置中写变量名或者相互依赖的内容,是不可维护的
  2. 可拓展性 : 每一个配置应当是原子化的,因为他们没办法通过其他配置项继承/组合,但是代码天然可以
  3. 图灵完备性 :能够描述复杂的逻辑 : 循环,递归等

最佳实践

我们何时使用配置化?

根据配置和代码的区别,我们可以总结出一个判断标准

  1. 配置化有利于快速更新 --- 这部分内容需要频繁修改 ? --- 是则使用配置
  2. 配置化有利于聚合分散元数据 --- 这部分数据在多处使用 ? --- 是则配置化
  3. 配置化没有复杂逻辑,简单易懂 --- 配置修改是否需要技术背景 ? --- 否则配置化
  4. 配置化只擅长于描述元数据 --- 配置的内容是否不包含逻辑 ? --- 是则配置化

我们如何设计配置?

设计配置化的目的就是扬长避短,发挥聚合,便捷的优势。避免使用配置描述逻辑

  • 配置内容需要原子化,配置之间不应该有耦合的关系
  • 配置的内容应该尽量语义化,降低阅读门槛
  • 配置的内容需要保证兼容性,越简单,就越容易被各种各样的业务情况使用

在下面的代码中,黄色部分就是建议提出到配置化,你可以看到,他们就是完全的数据,不包含任何逻辑。

switch(type){
     case : "生活文章"
         if(days.between("2025-5-1","2025-5-7")){
             push(type,0.2)
         }
         return
     case : "AGC文章"
         if(days.between("2025-5-1","2025-5-7")){
             push(type,0.1)
         }
         return
     case : "五一专题文章" 
         if(!days.between("2025-5-1","2025-5-7")){
             type = "普通文章"
         }
         return
}

经验启发和总结自诸多互联网大牛和前辈,小弟才疏学浅,难免疏漏或者误解,欢迎不吝赐教。