DTeam 技术日志

Doer、Delivery、Dream

一种轻量级的代码资产管理思路

胡键 Posted at — Jan 7, 2018 阅读

“代码管理还分轻量和重量?”,想必不少被标题吸引进来的朋友会有此疑问。在此,请容我解释一二:

  1. 注意,这里提到的是“代码资产”,而不是“代码”。
  2. 为了给后续的讨论设定一个基准,本文所指的“代码资产”主要是指企业自己生产的源代码、相关文档,以及由此产生的二进制制品。至于企业外购的各类软件包,不在本文讨论之列。
  3. 所谓轻量级,自然是指务实且容易上手。

既然设定好了讨论的范围,接下来就让我们进入正题。

为什么要对代码资产进行管理?

按照套路,在推销一种管理方法和手段之前,当然要先谈谈它的必要性。就本文涉及到问题域来说,主要是:

  1. 保障日常开发的顺利进行,避免出现各种不一致和潜在的错误。典型的:内部代码不一致、代码变更缺乏管理……
  2. 保护企业自身的知识产权,为组织代码设定各级保护机制,避免不必要的开发人员拿到不必要的代码。比如:对于每一个项目成员,都不得不 check out 所有代码才能进行开发。

对于第一点,大家应该并不陌生,因为到现在为止已经有各类书籍都从不同角度对它进行了阐述。不要以为它仅仅就是版本管理而已,其实它是:问题跟踪、版本控制和持续集成的一个综合体。要想保障开发的顺利进行,这三个方面必不可少。

对于第二点,我猜考虑的人可能并不多。一来是因为这个问题本来就不是大多数开发者关心的领域;二来不少公司才刚刚把第一个问题解决,至于这一个问题,那就先往后放一放吧。当然啦,还有一个更重要的原因是:专门从代码资产角度去讨论解决这个问题的文章并不多,导致看上去对于这个问题缺少行之有效的手段和方法。

其实呢,问题的解法已经散落于各类文章和书籍之中,只是由于作者没有突出强调它们针对知识产权保护方面的作用,导致不引人注目。而本文的目的就是将这些遗珠聚拢在一起,然后打包作为一个整体呈现于各位看官面前。这种做法姑且算作“知识集成”吧,:)。

代码资产管理的 3 个层次

回答完了“WHY”,那就让我们看看“HOW”。在这里,我仿造 CMM 的做法来对代码资产管理定义几个级别。因为在我看来,这几种做法属于层层递进,代表了管理方式的不断提高。

当然,为了呼应标题中的“轻量级”,以及前文所倡导的务实易上手,我会推荐相应的配套工具。由于我本人的 Java 相关背景,所推荐工具主要以 Java 系为主。但相信我,下文所列的各种方法并非语言特有的,其他成熟的语言和技术体系肯定有其对应物,读者可按图索骥自行探索。

Level 1:源代码

大多数像样的公司基本都做到了这一层次,正如我前文所言,连这都没有达到的公司只能算是“乌合之众”。

鉴于有很多开发书籍已经对此都有比较详细的阐述,本文也不打算对此说得太多。它基本上是这样一个循环:Ticket – 开发 – 集成。每个阶段都有值得注意的实践:

相应的工具列表:

还有一点容易忽视:代码质量。如果将代码视为资产,垃圾代码无异于不良资产,最好在 Commit 前就能剔除。像类似 CheckStyle、Findbug 和 Codenarc 这类工具可以提供一定帮助,如果使用 Gradle,可以考虑这个插件。当然啦,你也可以选用 Snoar。不过请注意:在切实提高团队的代码能力之前,不要对这里的工具抱有不切实际的幻想和要求。

最后,对于代码权限的管控,这里就不再赘述,这完全取决于你的开发组织和工程要求。基本上跟系统的访问控制如出一辙,没什么特别的。

Level 2:二进制制品

源代码级别的管控很好的解决了开发团队内部的代码资产管理,但开发团队之间的呢?比如:A 团队依赖于 B 团队的组件。最简单的办法当然是两个团队共用一个版本库或者依赖于类似 Git 中的 Submodule 这样的机制。但是,你有没有想过假如 B 团队的组件是一个基础组件,同时服务于 3+个团队,这种情况下该如何处理?

同时,我反对在这样的情况下继续以源代码形式来操作的原因除了显而易见的烦人之外,还有:

  1. 不同团队依赖 B 团队组件的不同版本,这种情况下,采用源代码来管理几乎无解。
  2. 不同团队使用的语言不同,并非所有团队对于 B 团队组件的依赖是以类库或 SDK 的形式体现。对于以 Restful API 形式依赖该组件的团队来讲,直接依赖源代码是不现实的。
  3. 不同团队涉密级别不同,可能 B 团队的组件属于组织的核心竞争力,即使在组织内部也不是所有人有资格和有必要接触到它的源代码。

很明显,以上的考虑已经将“源代码共享”这种方式排除出局了。此时,解决问题的办法只能是从二进制制品的管理上着手。

这里,分两种情况考虑。

Jar 包或 SDK

对于依赖管理,Java 开发领域已经有很成熟的解决方案:Maven 仓库。这比起自己编译加到处拷贝的方式不仅成熟而且要简单很多,想想你怎么解决不同项目对于组件不同版本的依赖问题吧!

看到这里,有经验的朋友应该已经明白我的意思:搭建一个私有的 Maven 仓库,然后将 B 团队组件发布到这个自有仓库中。其他依赖者只需像平常一样将相应的依赖添加到 build 文件中就 ok 了,这样一来,以上 3 个问题迎刃而解。

对于 B 团队,只需做好自己发布流程的管理;对于其他依赖团队,只需明确自己需要使用的版本就好。哇,整个世界一片祥和,皆大欢喜。

在这里,值得注重的实践:

还有,就是别忘了对私有库进行备份!这一节对应的工具有:

远程 API

随着组织的发展,团队的多样性也会增加,不见得所有的团队都采用相同的开发语言和技术体系。而且,随着类似“前后台分离”这样的开发实践的普及,即使同一项目组内部也会有不同的技术体系。这种情况下,Jar 包或 SDK 策略就难以实施。尤其是后者,总不能针对每一种开发语言都维护一套 SDK 吧。

此时,最好的解决办法就是求助于一种通用的调用机制,满足不同语言的调用需求。当前,这一机制就是 Restful API。明确了技术路线,接下来就简单了。因为时下流行的微服务架构对此早有解决方案:

说得直白一点,面对多语言开发团队之间的开发协作和管理,微服务是目前的最佳方案。在 Java 生态中,目前最全面的当属Spring Cloud。在 API 领域,其他值得关注的技术和工具还有:

最后,自卖自夸一下,对于 API 的 Mock 工具,可以瞧瞧我开发的dgate,简单且方便,;)。

Level 3:插件架构

解决完团队间的依赖,让我们再看一看这样的场景:项目组内部不同涉密部分的管控。这种情况介乎于“源代码依赖”和“二进制依赖”这两个管控层级之间:

  1. 从逻辑上来讲,他们是一个项目。只不过经过组织考验的同学可以接触到核心层的代码,而另一拨同学只能接触该他们接触的部分。
  2. 从物理上来讲,这又确实是不同的项目组。但这些项目组间的依赖关系比前面的依赖关系更紧密:后者的价值附着于前者之上才得以体现,单独存在则没有任何价值。

或许有同学会说,这本质上还是二进制制品的依赖问题,照搬前面的做法就够了。理论上是这个道理,但在集成方面,这种方式则显得过重,并且不利于项目组间的紧密协作。毕竟,如前所言,这在逻辑上来讲其实是一个项目组!

此时,我们可以求助于插件架构,将整个项目插件化,实现核心层和外围层的分离,进而实现相关负责人的分离。核心层负责集成和调度,外围层负责具体功能实现。但是离开了核心层,外围层几乎无法独立生存,自然也就没有价值。这种思路其实跟硬件的生产管控方式如出一辙:外围工厂负责单个组件或模块的生产,所有的装配在内部完成。

插件化很好的完成这样两个目标:

  1. 团队成员的分工协作。
  2. 保护好了核心知识产权的可控。

尤其是后者,它避免所有成员为了项目开发不得不接触所有源代码。即便是负责插件的同学,如果为了调试,他完全可以直接使用预编译好的项目工程,通过将插件放入相关的插件目录,让整个工程加载插件,进而完成调试。

如果为了更进一步控制,可以引入证书机制,发行开发用的临时证书,避免这类预编译的内部项目外流。当然,这一手段不是本文关心的重点。

并且,这里提醒一下:此处讲的插件是“真·插件”,即非开发用的插件,如 Grails Plugin 或 JQuery Plugin。后者本质上其实跟类库是一回事。这里的插件更像是一个设备的零部件,一个实实在在的功能实体。

在 Java 生态中,如果要实现插件架构,PF4J是一个不错的选择,值得你拥有。我曾经用它实现了一个简单的小工具,很方便。

当然,你也可以说,如今是微服务的时代,用微服务也可以达到类似的效果。对此,我不能说错。但,多一个选择总是不错的。

总结

本文主要从技术和架构设计的角度阐述了代码资产管理的若干方法,并针对不同级别给出了相应的技术方案和支撑工具,以贯彻我一直倡导的“可落地”方案。

至于其他,如某些大公司那般“不允许带 U 盘”等这样的管理制度,不在本文的讨论之列。这类机制虽有参考价值,但不具备通用性,一般需要根据实际情况量体裁衣。最关键的是,我认为“好的技术支撑会大大降低管理成本和制度要求”。这也是我将之称为轻量级的原因。

希望本文能给大家带来帮助,也欢迎各位指正!


相关文章