这样才是代码管理和 Commit 的正确姿势! | 研发效能提升36计
简介:效能提升从小习惯开始,这样才是代码管理和 Commit 的正确姿势!
专栏策划|雅纯
志愿编辑|张晟
软件交付是以代码为中心的交付过程,其中代码的作用有几点:第一,最终的制品要交付成什么样,需要通过代码描述清楚;第二,代码定义了系统和软件是怎样工作的;第三,代码定义了系统的运行环境是怎样的。所有这些都是围绕代码。
那我们的代码管理和软件配置管理应该怎样做呢?
我们先看一个例子。下图是某个团队的代码组织结构,这样的代码组织结构会有什么问题呢?
问题1:代码组的命名方式混乱
我们发现在最上层的目录中叫risk-managenment,这是一个系统,这个系统是风险管理。但是子目录写的是叫“qinglong”,那“qinglong”是应用还是团队,我不知道。然后下面还有一个玄武,下面还有一个aTeam,中英文混杂,这样的命名方式是很混乱的。
问题2:用代码块存储外部二进制文件
在android-sdks里面会存放很多sdk文件,这些文件是很大的,这个代码库存储很多外部二进制文件,我们知道在代码库直接存这样的大文件,对整个代码库的资源消耗是非常大的。
问题3:同一归属的代码保存在不同的代码组
在aTeam目录下有一个data-model,但是其他相关的文件都在玄武下,就是data-console、data-task、data-ui,我们不知道它具体是什么,但是我们知道这几个大概率是同一个应用或者是同一个产品,所以它在两个不同的层级也是不合理的。
问题4:公共库保存在子代码组里
再下一个是common-lib,通过名字来理解就是公共库,但是这个公共感觉只给玄武这个子代码组使用。
问题5:应用的文档(或测试)与应用分开存放
最后还有一个docs目录下有risk-docs和data-docs,一个是针对风险控制的系统,一个是针对数据地系统。那这个里面文档也是一个代码库,文档代码库和测试代码库,它和应用是分开存放的,这也是不合理的。
好的代码库组织形式是怎样的?
问题:假设所有的代码都保存在一个代码库,且所有人均可访问,代码库应该怎么组织?
我们认为代码库是可以分组的,代码组(+子代码组)+代码库=大库。
基于这个逻辑,我们再看看刚才那个例子里合理的代码组的结构应该是怎样的。
如上图所示,整个代码库是一个系统,这个系统有两个应用,一个是risk,一个是data。每个应用下面是有很多的服务和文档。它们有一个公共的Model,叫common-lib,这是被所有的应用所依赖的。所以我们把属于同一个应用的Git仓库放在一起,让common放到该有的地方去。不是按照团队,而是按照应用组划分,这样划分,结构就更加清晰了。这里我们稍微总结了一些实践的建议。
- 代码库的内容:
-软件的源代码(ProductionCode);
-将文档(和测试)的git库放到其相关应用组下;
-不要将制品(如系统二进制包)保存在代码库中,如果确实需要,以LFS或类似方式存放;
(小编推荐:云效代码管理Codeup为企业提供免费不限容量的LFS存储)
- 代码库的组织结构:
-按照系统、应用和模块的层次来组织代码库;
-同一个系统/应用层级的所有内容位于同一个代码组下;
- 代码库的可见性:
-通用代码库放在其通用级别都可以访问的位置;
-除核心算法等少数代码库外,建议对代码库的访问在同一系统/应用下对所有相关人员公开;
代码组织完了以后,开发者就可以围绕代码库来进行协作。整个代码库的协作过程就是:一切皆Commit。无论是rebase还是merge,都是Commit。
那对于Commit,我们有什么要注意的呢?
什么是好的Commit
我们总结了3点建议给到大家:
1.Samll
Git库要尽可能地小。尤其是目前的基础设施现状下,虽然你的一个仓库里可以放多个应用,但是维护起来的成本会很大的。还有管理方面,不要在Git上存储构建产物和其他二进制文件。把构建产物放在构建仓库上,虽然给别人方便了,却很难知道这个构建产物是现在的代码产生出来的还是之前产生出来的,这是很难去追溯的。对于二进制文件,如果确有必要(例如游戏的素材),建议使用LFS的方式来保存。
2.Linear
避免无意义的merge,尽量用rebase操作。其次是避免无效commit,有很多代码库commit记录很长,但是里面80%都是无效的,例如都是fix1、fix2这样的commit,都却不知道它具体做了些什么,这种显然是不合理的,对于这种冗长的commit列表,有时候可以在merge的时候squash一下。
3.Atomic
原子性,指操作的原子化。原子性有什么好处呢?一个Commit解决一个特定的问题,比如说我就是修复一个UTcase,或者是加一个UT或者是加一个功能,或者是加一个API,这些明确的问题对应到一个commit,很容易追溯。解决的问题不能很大,不能写了2000行代码解决了一个feature,一起提交,这是非常危险的。作为开发者,做的好的应该是快速有阶段性的成果,并且持续地有反馈,持续地贴近目标。反之,开发者的体验不好,相关协作者的体验也不好,因为别人不知道你做了多少了,很有可能跟你发生mergeconflict。
下面列举一些Commit的反模式:
1.无效的commit
如Mergebranch'develop'of https://codeup.aliyun.com/abc/xyzintodevelop第一个问题,在几乎所有公司里面都是随便拉开一个代码,本地和远程都有这种情况,本来一个rebase搞定的事情,这样做会导致很多无效的commit,甚至对commit追溯能力会产生很大地影响。
2.巨型commit
一个commit里面包含了大量的代码变化,且属于多个实现目的,就像codereview,有些人提的mergerequest,一下子过来3000多行代码,作为reviewer,你完全不知道他做了什么,这是非常危险的。
3.半成品的commit
如包含有基本语法问题或实现错误的代码的commit半成品的commit。例如,到饭点了,不管了,先提交一把。这样的代码连编译都过不了,这个显然是不好的,没有任何意义。
4.分支间的互相merge
最后一个是分支间的互相merge。从develop合到master,又从master合到develop,互相合来合去,一旦这种合并多了以后,commit就会很难追溯,因为不知道源头在哪。我们建议代码库应该有一个唯一的主干,单向往主干merge,尽量避免反向merge的情况。
(小编推荐:云效代码管理Codeup的主干开发模式,就提倡轻量的commit评审 和主干研发,帮助企业避免分支间的复杂合并~)
软件配置管理
问题:软件配置经常被修改,被发布,它属于代码吗?
软件配置其实是另外一种形式的代码。有可能大家在实际工作中配置不是存在Git仓库里面的,可能是在一个配置中心或者其他类似系统里面,但无论在哪里,本质上,我们可以把配置等同于某种类型的代码。
下图是大家常见的静态配置和动态配置,或者说启动相关的配置和运行相关的配置。
启动相关配置
- 启动相关配置是构建到镜像中或者作为启动参数传进去的。
- 启动之后不再修改了,不需要去动态监听它的变化。
- 对这类配置的修改,一般需要重新创建或者重启容器。
以此类推,哪些配置是启动相关的呢?比如DB连接串、容器CPU规格、启动模式等(比如有的压测应用启动的时候区分master模式和worker模式)。其它像DNS服务地址等,诸如此类的我们都认为是启动相关的配置。
运行相关配置
- 通常是通过监听某个服务或文件来获取和更新的。比如说我要看一下我的白名单是什么,我去读一下白名单。
- 配置的更新是不需要修改容器和Pod。
- 运行中的容器需要持续监听配置的变化,当有变化后自动生效、无需重启。
我们举一下场景实例说明一下:
- 大促时期调整日志级别,只记录ERROR级别的日志。
- 服务的黑白名单,为了限制某些IP的访问,将其列入黑名单。
- 特性开关,通过开关打开或关闭某个feature。
- 监控采样频率,由每分钟采样一次调整为每5分钟采样一次。
这些配置不需要也不应该每次修改都重新部署应用,他们都属于运行相关的配置。
我们再来看一个demo示例里面哪些是启动相关的,哪些是运行相关的。我们列举一下:
这是启动的时候就会需要的一个参数。
我们将secret文件注入到Deployment中,应用自动从文件感知secret的值,无需重启,因此它是运行时的配置。越内层的配置,修改成本越高。
从另外的角度看一下配置,它有不同的层次,代码、镜像、Pod和系统。代码中的配置位于最内层,修改成本是最高的。因此,如果是编码级别的修改,要经过所有的阶段才能上线。如果运行阶段的话,我是不需要动前面的部分。
最后,留一个问题给大家:运行环境相关的配置是属于哪一种?欢迎大家在评论区留言互动。
软件交付的终态是提供稳定可预期的系统,要做到这点,需要确保:1.运行环境的一致性;2.软件制品的一致性。所以下篇,我们将开始分享如何保证运行环境的一致性,以及环境中大家常见的痛点和应对方案。敬请期待!
本文为阿里云原创内容,未经允许不得转载。
这样才是代码管理和 Commit 的正确姿势! | 研发效能提升36计的更多相关文章
- (转)Git 提交的正确姿势:Commit message 编写指南
Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交. $ git commit -m "hello world" 上面代码的-m参数,就是用来 ...
- 提高php代码质量 36计
1.不要使用相对路径 常常会看到: ? 1 require_once('../../lib/some_class.php'); 该方法有很多缺点: 它首先查找指定的php包含路径, 然后查找当前目录. ...
- PHP之提升代码质量36计
转载:https://www.binarytides.com/35-techniques-to-enhance-your-php-code/ 1.不要使用相对路径 常常会看到: require_onc ...
- Spring Boot 2实现分布式锁——这才是实现分布式锁的正确姿势!
参考资料 网址 Spring Boot 2实现分布式锁--这才是实现分布式锁的正确姿势! http://www.spring4all.com/article/6892
- 这才是使用ps命令的正确姿势
这才是使用ps命令的正确姿势 前言 在linux系统当中我们通常会使用命令去查看一些系统的进程信息,我们最常使用的就是 ps (process status).ps 命令主要是用于查看当前正在运行的程 ...
- 【git】代码回退指定commit
[注意:如果提交的错误代码较少,可以在本地修改成 commit之前的正确代码样子,然后再提交一次即可.不用麻烦的操作回滚.] 开发人员错误将代码提交到gitlab的远程dev分支,回滚方法如下: 1. ...
- Git提交代码冲突:commit your changes or stash them before you can merge.
用git pull拉取远程分支代码时候遇到如下问题: error: Your local changes to the following files would be overwritten by ...
- Eclipse IDE 使用指南:Git失误提交代码,撤销commit操作
在Eclipse IDE使用Git Commit提交代码时把不需要的文件失误Commit了,比如.settings..classpath..project等文件. 如果是Commit提交代码到本地仓库 ...
- Git回滚代码到某个commit
回退命令: $ git reset --hard HEAD^ 回退到上个版本$ git reset --hard HEAD~3 回退到前3次提交之前,以此类推,回退到n次提交之前 $ git rese ...
- 代码在ie9中不能正确执行
<!DOCTYPE html> <html> <head lang="zh"> <meta charset="UTF-8&quo ...
随机推荐
- 常用加密及其相关的概念、简介(对称、AES、非对称、RSA、散列、HASH、消息认证码、HMAC、签名、CA、数字证书、base64、填充)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明 无 前言 在之前,一直是通过生活.工作零零碎碎 ...
- 简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
前言 最近简单学了下Rust,以我这种菜鸟水平,没感受到什么安全.性能什么方面的优势,只觉得概念太多,编译各种报错.暂时也写不出来什么玩法,索性对比下各种学过的语言的性能.部分语言很早之前学过,很久不 ...
- C# 12 拦截器 Interceptors
拦截器Interceptors是一种可以在编译时以声明方式替换原有应用的方法. 这种替换是通过让Interceptors声明它拦截的调用的源位置来实现的. 您可以使用拦截器作为源生成器的一部分进行修改 ...
- 记录-VueJs中如何使用Teleport组件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在DOM结构相对比较复杂,层级嵌套比较深的组件内,需要根据相对应的模块业务处理一些逻辑,该逻辑属于当前组件 但是从整个页面应用的视图上看, ...
- 利用Python 去重聚合Excel数据并对比两份数据的差异
问题背景 在数据处理过程中,常常需要将多个数据表进行合并,并进行比对,以便找出数据的差异和共同之处.本文将介绍如何使用 Pandas 库对两个 Excel 数据表进行合并与比对,并将结果输出到新的 E ...
- js判断对象数组等是否为空
//是否为空 /** * null undefined NaN false " " {} [] 为空 * 为空 true 不为空 false * @param {*} value ...
- KingbaseES数据库-生产环境慢查询性能优化案例
一.背景 在生产环境中,慢查询不仅影响系统.业务的正常处理.同时严重影响用户的使用感受. 二.表相关信息 2.1 表结构及索引: Table "yktcore.t_dtl_ymt" ...
- Ant Design Vue Tree 选中子节点同时半选中父级节点
需要实现的效果: 1.子菜单如果不是全部选中,一级菜单半选. 2.子菜单全选,一级菜单选中. 3.一级菜单选择,二级菜单全选. 4.没有二级菜单,则只控制一级菜单. 主要用到的属性是checked和h ...
- 基于UDP的服务器端/客户端
基于UDP的数据I/O函数 //成功时返回传入的字节数,失败时返回-1 ssize_t sendto (int __fd, const void *__buf, size_t __n, int __f ...
- NodeJS 实战系列:模块设计与文件分类
我们从一个最简单的需求开始,来探索我们应该从哪些方面思考模块设计,以及如何将不同的文件分类.之所以说"思考",是因为我在这篇文章里更多的是提供一类解决问题的范式,而非统一的标准答案 ...