摘要:程序员常会说:我最讨厌别人写的代码没有文档,我也最讨厌自己需要写文档。
有一个很老的梗: 我最讨厌别人写的代码没有文档,我也最讨厌自己需要写文档。
有这种想法的程序员应该算是一个老鸟了,对于大多数程序员来说,对于他们来说: 文档是什么。
对于大规模,超大规模的项目,并且历时很长,需要大量人员协同开发的项目,没有文档简直不可想象。但是由于时间紧,任务重,大多数的项目中的开发者都没时间写文档,而且,文档也不计入考核指标,导致开发者也没有动机写文档。这就造成了很多项目都缺少规范化文档,项目的交接和接口的对齐都是靠口口相传,接口定义准确度低,并且项目的整体开发效率低下。
作为一个文档爱好者的笔者来说,平常很重要的一件事儿就是将自己的工作归纳总结并整理成文档。即便如此,笔者也面临着很多问题:
需求很快就变了,光是改代码就已经耗尽了我的精力了,哪有时间同步文档。
要么先改代码,要么先改文档,总之文档和代码之间存在一个不一致错位期。
除此之外,由于我们主要从事AI相关能力的研发,所以在开发过程中还存在以下特殊的问题:
开发者不得不写大量重复的网络编程相关的业务代码,这些代码的质量通常不高,并且在后期的反复修改中变得越来越臃肿,从而难以维护。
因为重复的业务代码开发工作占比过大,严重压缩智能化研发人员在AI方面的精力投入,这就导致了企业投入大量人力却无法得到好的效果。
经过不断的摸索和实践,AIMS项目组采取了一套文档驱动的开发模式,可以有效地解决上述在项目中广泛存在的问题。
1. AI接口开发的特点不同于传统API的CRUD接口的开发,AI的开发模式通常包含了以下步骤:
数据清洗;
模型训练;
参数调优;
API上线。
前三项都是我们组的强项,也是我们的工作重点。但是在实际工作中,却是API上线消耗了我们的大部分精力。AIMS项目组大都从事的是AI方向相关的工作,对于API的相关库flask、tornado也不是很熟悉,这就导致开发人员需要花大量时间去了解网络编程相关内容,开发出来的程序质量可能也不是很高。而且,在接口开发后,还需要准备一份Swagger UI给前端的开发人员,这又是一项繁重的工作。为了解决如上所述的痛点问题,AI接口开发就需要满足以下两个特点和需求:
API的核心函数是现成的;
需要将API开发工作量降到最低。
2. 文档驱动的开发模式我们发现API的接口文档中已经包含了实现API接口的所有信息。也就是说,API接口文档的信息熵和API接口代码的信息熵是一样的。因此,我们只需要完成代码或者文档中的一项即可,另外一项可以交给框架自动生成,这就避免了把同样一件事情重复做两遍的问题。考虑到文档不论从可读性、易写性还是可维护性都胜过代码,我们决定写文档,然后根据文档生成对应的代码。
我们参考了OpenAPI Specification3.0.1标准,以及JSON Schema定义了一套API描述语言,开发者基于这个API描述语言撰写API文档。当完成文档时,API的开发工作也随之完成,大大节省了API接口的开发工作量。经过初步估计,使用文档驱动模式开发一个API接口,通常可以减少40%–70%的工作量。
3. AIMS框架介绍为了实现文档驱动的开发模式,AIMS基于tornado实现了一套Web后端框架,如下图所示。
3.1. Application Router组件每一个API都会有一个路径,即一个正则表达式[1]。一个微服务中的多个API的所有路径会组成一个API路径列表。
当Application Router接收到请求时,会根据请求中的URI在路由表中查找。如果匹配到某一个Request Handler,路由模块会将请求转发给响应的Request Handler。如果所有的路径都不匹配,则返回404结果[2]。
3.2. Request Handler组件Request Handler处理来自Application Router的响应,是AIMS架构的核心,而Document则是Request Handler的核心。在AIMS架构中,Document是指函数的文档,即__doc__。Request Handler是在服务的启动阶段动态生成的。
事实上,在AIMS架构图中,只有Document是开发者必须写的[3]。其它的组件都是由AIMS提供的,或者是由Document自动生成的,所以AIMS开发模式也被称为文档驱动型开发模式。开发者只需要维护Document即可,从而实现减少开发者工作量的目的。
3.3. Watchman组件Watchman组件可以通过设置来订阅某些接口的异常,当被订阅接口出现任何异常时,Exception Handler便会触发Watchman组件。
因为Recorder组件记录了Arguments,Watchman甚至可以自动产生一个可以复现该问题的测试用例,方便开发人员定位问题。
3.4. Recorder组件