先说一个经典案例:程序员小a长期在A项目进行开发编码工作,突然同部门下的B项目紧急缺人,小A被调到B项目进行开发,小a在看过多轮“五花八门”的项目文档之后,终于打开了idea,结果发现pom文件一堆爆红,排查后发现是自己本地的配置的A项目组内网远程仓地址缺少很多B项目代码的依赖,东问西调跑到外网进行依赖补全,解决了B项目的依赖问题;终于进入编码阶段了,结果在面对使用Es问题时,B项目里的client的版本和A项目不一样,其中关于Es的嗅探、超时机制、DSL编写方式、API调用也是B项目单独封装的一套风格和接口层,于是小a又费上时间去看返回对象的结构、封装逻辑、API的参数调用逻辑以此来达到更好的使用效果……
虽然以上这个过程大家都见怪不怪,团队以项目为单位,割裂严重,虽然同一研发部门,但是换个项目好比换了个工作,甚至同一项目下,不同模块、功能组间代码五花八门,这太常见了,这样的常见背后的问题是:
虽然功能、产品、项目五花八门,但其实技术重叠部分较多,而这些重叠部分又每每在以上三点上消耗,形成无形的成本浪费,导致效率、质量低下。
其实经常思考一个问题,一个技术团队,或者说一个企业的研发部门,它的核心资产是什么,答案可能有很多,例如团队人才、团队信任、团队默契……等等,但是其实这些都是比较虚的东西,不可否认这些内容确实重要,但是它是一种“软实力”,并不是“资产”。所谓“资产”是经过沉淀后,可以直接拿来重复使用,从本质上降低生产成本的“实物”。
企业里的研发部门、技术团队其实更多的是软件/互联网公司的生产部门,好比实体产业的生产车间。生产车间可以通过更新车床、设备来提高生产力,这里的车床、设备即是“资产”,那么研发部门的“资产”类比一下就出来了,就是部门级的技术标准、工具。
其实大多数所谓的“技术领先”、“技术创新”的本质都是缝缝补补,真正高大上的技术或者技术研究终究是掌握在少数人和企业的手里,能够快速响应和支撑市场才是大多数企业研发的主题。更多的是用“马斯克”思维,别管low不low,能快速响应市场,快速支撑成品才是硬道理。
技术资产是经过多轮项目和实战抽离出来的实体“经验”,确定性和稳定性都是经过认证的。同样的问题早已经在技术资产层面解决和规避过了,不会在新项目中再现。
相比于硬编码开发新项目,依赖的是人员的经验和技术素养,基于“积木”式的组装代码则依赖更具体、更量化的技术组件,质量也更为可控
站在成本的角度考虑,技术资产的存在能够大幅降低人效和沟通等隐形成本。
技术资产站在编码研发角度无非两类,在编码一级对应的是二方库,在架构一级对应的则是技术底座。
这二者对应我在领域驱动那部分说过的通用域和支撑域,其实不论是否使用领域驱动,统一的二方库和底座都是一个团队的必需品,一定要有是第一要素;
其次就是建设粒度,至于建设到何种粒度,取决于团队规模和项目诉求,但是不可好高骛远,一个以做具体业务系统为主的企业或部门却要去搭建一个通用的“数据中台”工具这种就是好高骛远,系统级开发的,甚至连服务器群组都不具备的规模,一个稳定且统一的二方库,基于SDK包构建项目足够了,不要为了做而做。
最后就是执行力度,既然费力建设了,就要“快刀斩乱麻”,一鼓作气进行全量替换重构或者逐步重构替换,不要以“工期”、“进度”、“等新项目再开始用”等理由,让已经完成的技术资产处于闲置状态,因为技术资产是需要通过使用来驱动演进的。只建设不使用,也是“为了做而做”。
关于二方库的概念来自于阿里的开发手册,大体的定义是这样的:
名称 | 含义 | 所属级别 |
一方库 | 当前项目工程中的各模块的相互依赖,是本项目中的依赖。比如一个多module项目中,一个module依赖于同项目中其他一个或多个module | 项目级 |
二方库 | 公司内部的依赖库,一般指公司内部的其他项目发布公共jar包。 | 公司或部门级 |
三方库 | 公司之外其他组织的开源库, 来自第三方的依赖比如apache、google等发布的jar包。 | 互联网开源库 |
根据定义,二方库的概念和我之前在领域驱动文章中提到的通用域重合。
首先,就是要建立完善且稳定的内网仓库,用以存放经过认可的三方库依赖包和自身开发的库包,研发内部均以此仓库为准。
以后端Java系为例,二方库最经典的表现形式就是spring-starter,技术资产的搭建主要是建设内网稳定的maven仓库,maven内网仓库主要包含三部分:
针对三方库这一点,没有其他的操作,只需要建立完善的公网maven到内网maven的通道即可,针对三方库,需要做的就是保证更新策略,能够及时更新内部maven库中的依赖包,保证在开发需要用到三方库时不需要再费力寻找;另外在通道同步时,要进行一定的安全过滤,对于已经发生的安全漏洞、严重问题的依赖,要建立完善的及时拦截和更新策略,从项目伊始就降低安全问题的修改成本。
二开的内容主要是基于三方库或其他开源项目的二次开发,内容包括:进行内容整合、进行功能扩展、进行内容改造、修复已知问题;例如可以基于自身部门项目特点,集成常用的依赖,建设后端项目脚手架。根据自身的业务诉求,对flowable的流程处理进行封装叠加等等。
需要注意的是,因为在官方外掺杂了个人意志的代码,多多少少就会增加一些信息复杂度,所以对于二开的库类,需要配套完善的文档和使用说明,文档和使用说明是二方库开发的一部分。
基于企业或团队自研的成果,要保证其安全性和私密性,仅允许内部使用,是团队技术的核心竞争力;这里的源代码开发并不一定指是从0到1的开发成果,某些二开组件,只要是在某些基础之上进行创新型、独有的改造也可以归为此类。
二方库涉及的范围一般为:
组件 | 描述 |
通用对象处理工具 | 例如、文件上传下载、文件操作、时间、日期处理、字符串处理、缓存管理工具、线程管理工具、集合、数组处理工具、协议通信等Util类工具 |
统一的安全过滤机制 | 统一的AOP切入,针对各类注入、透传等内容的拦截、参数校验规则注解等安全类问题处理机制 |
各类三方组件的适配对接 | 各类数据库、存储库、消息管道的第三方组件的适配starter以及与其相关的操作适配,例如Mybatis的SQL方言统一兼容等 |
代码脚手架 | 统一的项目parent,统一集中控制项目中的三方依赖 |
日志监控机制 | 各类监控日志、系统运行情况的输出机制 |
…… | …… |
这里叫技术底座也颇有些“吹嘘”嫌疑,其实也可以理解为是“重型”二方库,说白了就是现在很多企业吹的自研XXX平台、XXX引擎,是一种可适配多场景、多应用、集成项目时使用的重型技术组件。
底座的定位与基本逻辑
这里技术底座并非是平台或业务系统,它的定位本质上还是组件,只不过是适用范围更为宽泛,体系更为完整。
应该是可以节约开发成本的技术性平台,它的底层逻辑是为更大、更复杂系统提供能力支撑,使用时并不能像二方库一样开箱即用,面对不同的使用方或场景,项目代码是需要进行一定的适配和开发。例如研发一套前端低代码平台,以提供“拖拉拽”的形式省去重复的前端代码编写,以技术能力来简化实现;又或者开发一套轻便级的编排响应,配合简单的元数据定义,以此来支撑多数场景下,系统与第三方数据的交换对接。
开发原则主要有两点:
面向技术抽象开发
技术底座应该是面向抽象内容设计,而不是具体的业务设计。具体到实现就是,面向数据结构和能力逻辑设计而不是具体的系统功能设计。
技术底座的实现是为了在架构层面进行有效的聚合,能够作为架构层面的拼接“积木”,所以要提供更高层次的抽象,以便有更广的适用范围。跟它的定位一致,要以提供能力为目标,而不是为迎合某个项目或产品为目标,这一点在设计和演进中一定要把控。
“站在巨人的肩膀上”
直白的来讲就是尽可能基于稳定版本的开源组件进行二开,“站在巨人的肩膀”上前行。一定要避免研发的通病——手搓核弹。
首先明确建设技术底座的原因是减少技术差异性和降低开发成本,所以一定不要不过一切的大量投入产研能力去从零开始,原本为了降低成本的事,反而成了增加成本,是一种本末倒置;其次就是一个比较现实的问题,不要过分相信自己的能力,这种通用型的技术底座,或多或少地都要涉及到IO、编译原理等等较为深的技术点,直接使用成熟的开源项目进行上层封装,或者整合相关的组件是明智的选择。
其实对大多数企业和部门来讲,一个完善的二方库就足够了,没有必要建设技术底座。
即便是采用成熟开源项目二开,也是极其消耗资源的投入,而且很容易在开发过程中走入不可控局面,最终成了一个“玩具demo”和汇报PPT的素材。精力资源也投入,项目反而没有多少获利,纯纯是研发人员自己的狂欢。
决定建设技术底座前要考虑如下因素:
如果一个部门或企业,项目多为单体的或者特殊领域的定制内容,并没有整建制的框架规划,则没有必要建设和使用技术资产;另外多项目组、产品间是否通用诉求较多,例如ETL过程、编排响应、繁重的页面开发、大规模的算法模型使用等等;如果建设和使用技术底座后,并不能明显地降低开发成本,只是开发流程或者开发形式上发生了变化而看起来像降低了开发成本,则要慎重决定。
与项目一样,技术资产也是迭代前行的,它需要项目的验证和反馈,然后不断更新和迭代,然后再反过来支持项目的开展。
在建设上,需要将技术资产的开发分为稳定池与开发池,稳定池中存放的是能够放心使用且适配完善的发布版内容;二开发池则是需要不断接收不同项目、不同开发任务的需求和反馈,进行设计开发的“测试版”;
例如,某个项目需要在各子系统启动后向主系统汇报运行情况,而另一项目需要新增一个系统运行状态页面,此时就需要结合多个系统相似的诉求,设计开发一个系统监控的starter发布使用,由于项目周期原因等因素,可先一步完成项目,再技术组件形成后考虑是否迭代替换或不替换,而只在后续有新项目开启时使用此starter; 又或者在使用过程中,发现某个组件包的功能存在问题和风险,例如安全问题等,可以针对该组件包进行加固改造,然后发布新版本并全局声明替换。