我近两年来经历了从演示到枪毙和从验证到上线两个不太完整的项目开发。发现在这个过程中由多人协作而产生的功能合并和分支合并问题,越到后期、临近上线、无暇解决问题的焦虑时刻时,就越严重、越恶劣。本文主要想总结之前遇到的问题,并尝试讨论缓解这些的问题。
分支与同步有多难
项目随着进度推进与产品方向的要求,不得不在某个时间点开分支,以及处理各个分支之间同步。在项目上线后,反过头来再看,光一个功能需求和Bug修改就经常包含代码、数据、资源等多个小组的多次修改,而一个版本又包含多个功能。打包前的信息确认费时费力还容易遗漏,万一出错,整个发布流程还要重走。
- 分支多:在研的主干和版本分支、QA使用的预发布分支、先行玩家测试的体验服分支以及线上分支
- 节奏多:不同版本的功能和Bug有着不同的开发和测试节奏。这些版本节奏还在时间上经常重叠交叉冲突。
- 周包每周更新,需一周测试
- 小版本每两周更新,需要一周测试
- 大版本每个月更新,需要两周测试
- 冲突多:代码因不同人先后提交导致合并难,数据和美术等二进制资源因无法自动合并而必须手工同步甚至重新制作
- 耦合紧:如果一个需求或Bug导致客户端和服务器同时修改了同一条协议,那么客户端和服务器就必须保证各个分支的同步状态步调一致
- 提交乱:部分同学常常一次提交多批修改,不能保证一次或多次提交只对应一个问题。更普遍的情况是提交内容极度笼统,无法从提交说明推断对应的问题是什么
新方案的美好的展望
- 每个需求或Bug必须有一个可追溯的唯一ID,比如Teambition的TB号,或者自动生成一个
- 程序策划和UI一次只能提交一个需求或Bug的修改,并在提交说明中按照固定格式携带需求ID
- 同一个功能或Bug的返工算新功能或Bug,分配新ID。新的提交要携带新的ID
- 每个人的所有提交说明不能重复。这条是为了唯一标识每一条提交,如果使用Git工具的cherry-pick而不是merge,那么git分支区别查询就失效了,所以依赖提交说明来表示唯一。对于SVN的不同分支来说,也只能使用唯一的提交内容来表示分支差异
- 如果是Git管理,开发需求或Bug时最好本地按需求开分支,提交前先本地合并。原因是一个人经常存在同时开发多个需求的场景,本地分支用来管理需求和方便交错提交
- 在研的主干分支包含全部未废弃功能和提交
- 尽量提交文本类修改和资源。比如Scene和Prefab文件提交以YAML序列化的资源。这条为了方便使用自动比较和合并工具(UnityYAMLMerge)
- 需求规划表要同时表达需求、分支版本和提交状态(多条)的信息
- 一个需求再彻底完成前,除只存在主干在研分支上以外,不能向其他分支同步
- 其他版本分支的需求如果提交条目不等零且少于主干提交条目,那么这个需求就是未完成同步的。这个分支这时候是不可用(打包)状态
- Editor相关功能跟游戏功能拆成两个工程独立,解耦游戏运行时逻辑和游戏制作时逻辑,不然后期上线后Editor有修改就算跟具体游戏逻辑无关,也不敢上传,心智压力大。
上述这些条目,带来最直接的缺点是每个具体操作的人的工作量和难度大幅上升了。但是这些额外的操作和信息可以帮助项目减少错误发生。尤其是我在后期直观的感受,大量同事花费巨大的代价在Debug由同步引起的问题,弥补同步错误,等待因同步错误造成的重新发包,多个流程小组相互等待而不知道哪个环节卡住了。另外哪个版本都上了哪些需求,改了哪些Bug只能通过口口相传,确认流程复杂又不明晰。如果充分利用这些额外的信息,可以制作像下图一样的版本需求同步状态图,会不会减少一点心智压力。
可能遇到的困难
- 生成的资源不好归属哪个具体的需求。比如合批的图集,自动产生的代码。这些最好打包机自动做,不要手动生成上传
- 资源重定向问题。比如UI引用了图集的FileID,而打包机自动导出图集时导致FileID变化而Missing。导出程序需要处理自动重定向
- 大量需求和提交整理与关联费时费力。应该提供脚本,自动拉取需求列表和提交日志,进而关联和导出报表
- 具体操作时没办法保证提交日志的质量。Git或SVN注册Hook,不满足提交说明的规则就阻止提交
附录:经验项目生命周期中工程和分支的演化表
时间点 | 内容 | 想解决的问题 | 新产生的问题 |
---|---|---|---|
演示 | 服务器、客户端、美术、地编、动画、策划等工程 | 降低各个组之间的交叉影响,代码和资源都发布到策划工程 | 程序策划代码不同步,查问题略麻烦 |
验证 | 程序从主干新建预发布分支,专人在打包前合并 | 提供略稳定版本 | |
评审1 | 程序各自从主干Cherry-pick具体功能到预发布分支 | 非作者不好解决合并冲突 | 多人先后Cherry-pick也造成大量冲突 |
策划新建预发布分支,专人合并修改 | 提供稳定的配置数据 | 策划、美术资源大部分无法通过svn合并,手工合并费时费力易出错 | |
期间由专人从预发布分支建立了多个功能版本,过后即废弃,主干依然是主力分支 | |||
从美术工程拆分UI工程 | UI资源修改与打包频繁,美术工程太庞大,UI资源相对独立 | ||
内测 | 主干-预发布-内测分支,主干研发功能进度超前内测分支,内测分支由组长管理合并 | 使主干研发进度相对独立,少受内测影响 | 主干与分支差异越来越大 |
内测分支的bug单独从主干Cherry-pick到预发布和内测分支,内测过后废弃分支 | 合并困难 | ||
评审2 | 频繁大量新建与返工的需求导致原预发布分支废弃,重新建立预发布和评审分支 | 对评审提供略稳定版本,合并困难 | |
主干功能研发进度大幅超前评审分支,各个分支功能与资源均有明显差异 | 存在即将废弃又不得不在评审分支里的功能以及开发工作和资源管理 | ||
公测 | 程序单独从预发布新建周更新、小版本、DLC分支,通过merge方式合并到预发布分支,通过cherry-pick方式摘回主干 | 给各个节奏上线的功能分类,解决主干无法合并的问题 | Merge行为会意外的携带其他人不完整的提交;Cherry-pick方法会产生遗漏,对每个操作人要求甚高 |
主干分支已经无法打出可以运行的安装包,仅供编辑器验证功能使用 | 主策、PM、组长花费大量时间与精力核对各个版本与分支功能的提交与合并情况,仍常常出错 | ||
从预发布新建体验服和公测分支,由预发布->体验服->公测分支依次测试和发布 | 发布流程要求 | 一个环节同步出错导致大量时间浪费 | |
紧急修复Bug要单独提交预发布、体验服、公测和主干,无法合并 | 打乱流程的提交导致下次merge时产生冲突 | ||
提交内容必须包含问题编号和问题标题,不得合并提交多次修改,注册Hook | 减轻回档和摘取的压力 |