代码还是数据
这段逻辑是代码还是数据,对我来说从来都不曾是个问题,直到遇上了这么一个项目。
背景
一般来说,我们写程序,都是连同测试代码一起提交,至少也是一起代码审查。有一个关于收费的项目,偏偏反其道而行之。这个项目的一些背景如下:
- 收费的逻辑通过DSL配置在代码库中。
- 对于这些逻辑,没有单元测试,但是有集成测试。
- 集成测试里面包含了request与response,在测试的时候回放request并比较response是否匹配。
- 集成测试是json文件,放在S3上。
如果有一个对费率的修改,那么程序员除了往代码库中提交修改后的DSL,还会往S3更新json测试文件。这就带来了几个问题:
- 代码审查者只能看到代码库的变动,除非再去S3上看测试数据,不然并不知道这段DSL的测试情况。而在S3上看测试数据相对比较繁琐,这样的机制可以说是鼓励代码审查者不去看测试数据。
- 没有版本管理(或者说由于S3与代码库的版本管理机制差别较大),导致这些测试不容易追踪,也不容易查看历史记录。
- 持续集成通过与否依赖于S3的数据。所以集成测试失败时,不一定能在解决时复原到失败时的状态。
- DEV提交代码和更新测试数据这两步变成了一个原子操作,需要一起完成,至少在代码提交后、CI上运行集成测试之前更新S3。而在提交代码前更新S3又有破坏别人正在运行测试的风险。
那么,为什么这个项目会选用这样的方案呢?原来,在设计的时候是这么考虑的:费率修改的需求来自于产品经理,希望测试数据能够由PM们提供。所以将会开发一个面向PM们的小系统,后台就是这个S3数据,这样的话到时候修改费率,就增加了一层来自需求方的保障。如果测试数据来自于代码库,那就很难通过页面来修改并提交代码库了。初衷还是不错的,但是仔细推敲下来,DEV自己的测试哪儿去了?这样运转起来后,是不是只会养成DEV把测试推给PM的习惯?
方案
我觉得这里面有一个误区,就是把DEV自己的测试和PM的测试混为一谈了。实际上应该将它们分开来。为什么呢?DEV自己的测试其实本来就应该是代码的一部分,应该保存在代码库中。而PM的测试其实是对程序员测试的补充,也应该算是代码的一部分,如果能够放在代码库中固然是好,但是我们也不能对所有的PM们都抱有提交代码这样不切实际的期望,所以在这种情况下,S3算是一个权衡的方案。而CI上应该有两步,其一是DEV的测试,其二是PM的测试(在PM修改测试数据的小系统还没上线之前,可以暂不配置这个测试)。它们之间是顺序执行还是并发执行倒是无关紧要。但这样也有不尽如人意的地方:
- 修改代码需要DEV和PM同时协作,最好一起修改,否则CI会红。但这正是DevOps运动所提倡的,不是么?
- 背景一节介绍的一些缺点在PM的测试中仍然存在。PM修改测试数据的小系统需要更加完善的版本管理和审计。这部分的开销可能还不小。
顺便提一句,传统的PM测试数据是由DEV提供一个CSV格式,让PM填完之后由DEV添加到代码库中。在忽略DEV和PM的用户体验的情况下,这也是一个可行的方案。
小结
到底什么样的逻辑应该进代码库,什么样的逻辑应该持久化呢?其实我们应该把逻辑区分为程序、配置和数据。
程序:
在代码库中,提供服务的主要功能。对其的修改通常都是改bug或是引入新功能。
配置:
在配置服务器中,但是配置的默认值很可能是在代码库中。经常需要修改,修改其值可以让程序表现出不同的处理逻辑。需要易于修改。
数据:
在持久化存储(一般是数据库)中,因用户而异,数量可能会比较大。随用户的操作而变化。需要有备份机制。
反推到上文所说:“收费的逻辑通过DSL配置在代码库中”。这段逻辑,也许应该是配置而非程序,因为它会经常需要修改。收费记录毋庸置疑,一定是数据了。从逻辑分类的角度上出发,你是否会发现其实自己现在的代码库中包含了太多的配置?
另外,虽然代码库似乎也可以用于配置或数据,但是最好还是别这么干,这里有一篇stack overflow的问答,解释得挺清楚的。