主干开发(Trunk-based)与 GitFlow:每周发布的比较
主干开发(Trunk-based)与 GitFlow 在每周发布场景下的比较:权衡合并摩擦、发布可预测性、热修流程和 QA 稳定性。

为什么在错误的分支模型下每周发布会变得混乱
每周发布听起来很简单,直到有一周你错过了发布。周四构建“差不多准备好了”,QA 在周五晚些时候发现问题,周一变成了清理日而不是一个干净的开始。
一个主要原因就是你的分支模型。它决定了变更何时合并、在哪里集成,以及在出问题时你能多快恢复。如果把集成留到周末,你要付出的代价就是合并冲突、意外的 bug 和不稳定的测试环境。
当工作流在与你对抗时,通常会感觉像这样:
- 合并花费的时间比实现功能本身还长。
- QA 测试的是错误的变更组合,因为分支发生漂移。
- 发布变成了协商而不是例行操作。
- 热修触发恐慌,因为没人确定还会有哪些东西一起发布。
- 人们不再信任 QA,开始要求例外处理。
分支也会影响 QA 的稳定性。如果 QA 不断在半集成的代码路径之间切换,它就会成为一个移动的目标。即便是优秀的测试人员,在系统每隔几小时就改变一次,或一次晚合并悄悄替换了他们昨天测试的内容时,也很难给出明确答案。
这就是为什么团队在转向每周发布节奏时,会把主干开发(trunk-based development)和 GitFlow 放在一起比较。真正有用的问题不是哪种更流行,而是哪种能减少合并痛点、保持发布可预测、让热修快速安全并让 QA 有意义。
假设一个小到中型团队,一个共享仓库,CI 在每次推送时运行。也许你发布的是一个 web 应用和一个 API;或者团队的一部分在像 AppMaster 这样的可视化平台上构建,同时仍像其他产品团队一样管理生成代码和部署。
如果你当前的流程导致周末出现大规模合并,你的每周节奏会不断推迟。如果流程鼓励小而频繁的集成,每周发布会变得乏味(以最好的方式)。
用通俗语言看主干开发和 GitFlow
这两种方法都只是一些分支使用规则,让工作能从“进行中”变为“已发布”而不会混乱。真正的区别在于你保留多少长期分支,以及工作在与别人的代码见面前会保持多久的独立状态。
- 主干开发把几乎所有东西都靠近
main。 - GitFlow 为正在进行的工作和发布稳定性保留了不同车道。
主干开发(通俗说法)
主干开发把 main(主干)当成中心。大家在短期分支上工作(几小时到一两天),频繁合并回主干,并保持 main 在可发布状态。
如果某件事还没准备好,你要么把它做得足够小以便快速完成,要么把它放在功能开关后面,这样代码可以安全发布但对用户不可见。
GitFlow(通俗说法)
GitFlow 通常有一个长期存在的 develop 分支,功能工作先在这里落地,然后从 develop 分出功能分支。接近发布时,你会切一个 release/* 分支用于稳定和测试。如果生产出现问题,通常从 main 切一个 hotfix/* 分支来修复,然后再合并回去。
这种模型优化的是分离:正在进行的工作可以继续在 develop 上,而发布分支则专注于测试和修补。
很多痛点来自常见的误解:
- 认为主干开发等于“不要分支”(其实仍然用分支,只是寿命短)。
- 使用 GitFlow 但从不真正创建 release 分支,使得
develop变成了一个不稳定的预发布区。 - 让功能分支存在数周,这会把任何模型都变成合并灾难。
- 把“可发布”误解为“一切都完成”而不是“安全可部署”。
合并摩擦:真正导致它的原因
合并冲突通常有两个来源:分支存在时间太长,以及多人在相同区域同时改动而缺乏协调。
主干开发和 GitFlow 之间最实用的差别是工作在与其他工作见面前会独立多久。
在主干开发里,变更频繁进入主线。PR 更小、diff 更小,冲突早早显现且上下文还新鲜。冲突仍然会发生,但通常更容易解决,因为你是在对几行代码进行协调,而不是处理两周的漂移。其代价是需要纪律性:保持构建绿色、保持变更增量并把 main 当成产品来对待。
在 GitFlow 中,功能工作可能较长时间不会影响其他开发者。成本在后期体现为更大的合并事件:功能合并到 develop、develop 合并到 release/*、release/* 合并到 main。每一步合并都是更大规模的变更碰面,所以冲突更可能且更难解开。对于每周发布,这类大合并往往出现在团队需要测试的时间段。
代码审查的负担也会变化。主干开发通常意味着更多但更快的审查。GitFlow 则常常意味着更少但更沉重的审查,外加在发布合并期间大家都疲惫时的额外审查时间。
无论哪种模型,都可以通过以下方式降低合并摩擦:
- 保持 PR 小且专注(一次只解决一个目标)。
- 就高风险区域达成所有权(迁移、共享配置、核心 UI)。
- 每天拉取上游变化,避免漂移。
- 如果某个文件总是“烫手”,两人结对修改而不是并行抢占造成冲突。
如果冲突感觉一直存在,通常是一个信号:你们整合得太晚了,不是 Git 的问题。
每周节奏下的发布可预测性
可预测性意味着三件事:你知道发布何时进行、你知道发布内容是什么、你知道发布前必须通过哪些检查。团队通常因为把范围决策和最后时刻的修复混在一起而错过每周发布。
在主干开发里,当 main 保持绿色时,每周发布更可预测。你频繁合并小变更,并用功能开关来控制范围,而不是长期分支。这样即便某个功能未完成,周度发布列车也能继续前行。代码可以入库,但面向用户的行为保持关闭直到准备就绪。
质量门通常更简单,因为只有一个地方需要验证:
- 自动化测试必须在
main上通过。 - QA 测试一个稳定的候选构建,而不是最近一小时内随意落入的内容。
- 回滚和放量步骤有书面记录。
- 有明确的发布截止时间(即便提交继续落入)。
在 GitFlow 中,可预测性来源于 release 分支充当冻结区。你选一个截止点,创建 release/*,只允许为发布所需的变更进入。这个边界有帮助,但会增加协调成本,因为修复通常需要在多个地方应用(release 与 develop 都要打补丁)。
未完成的工作处理方式不同:
- 主干开发:合并安全的部分,把剩余放在功能开关后面。
- GitFlow:把未完成的工作留在
develop,并把它排除在发布分支之外。
举例:如果结账改进在周三只完成一半,主干开发可以合并安全的重构并把新 UI 隐藏起来。GitFlow 通常会把它保留在 develop,本周发布不包含它,下一周再完成并发布。
热修流程:到生产的最快且安全路径
热修不是常规工作。它需要速度、低风险,以及一条能把更改带回团队工作主线的路径,以免重复修复同一问题。
先问一个问题:当前生产环境运行的准确代码是什么?如果你不能在几秒钟内回答,热修就会变成猜测。
主干开发下的热修
主干模式下热修看起来更简单,因为 main 被视为真实来源。
常见流程:
- 从生产的提交(通常在
main)切出短期分支。 - 做尽可能小的修复(如果可以,添加测试)。
- 快速合并回
main。 - 从
main部署并打 tag。
因为团队频繁向 main 集成,热修自然成为下一次每周发布的一部分。主要风险是违背“main 可发布”的规则,让半成品混入 main。功能开关能帮助,但默认应关闭,并在验证热修时不要启用无关功能。
GitFlow 下的热修
在 GitFlow 中,热修通常从 main(生产)开始,然后必须合并回 develop,以免修复丢失。
一个安全流程:
- 从生产 tag 的提交切出
hotfix/*分支。 - 修复并发布(可以直接从 hotfix 分支或合并到
main后发布)。 - 将热修合并回
main,也合并到develop。 - 如果存在
release/*分支,也合并到它。
最常见的失败是忘记其中一次合并。一周后 bug "回归",因为 develop 从未收到补丁。
一个简单规则能防止大多数问题:热修只有在合并到所有将来会再次发布的长期分支后才算完成。
如何让 QA 环境稳定(又不阻碍团队)
当“QA”意味着“当前随便部署的任何东西”时,每周发布就会分崩离析。为你的环境命名并给每个环境分配明确职责:dev 用于快速反馈,QA 用于团队测试,staging 用于发布检查,prod 用于客户。如果你不能在一句话里解释一个环境的目标,人们就会错误使用它。
防止移动目标的规则
稳定的 QA 更关乎你部署的内容,而不是分支模型本身。
在主干开发中,不要把随机提交部署到 QA。部署一个固定的候选(带标签的构建、构建编号或短期的 release-candidate 分支),每次都让它通过相同检查。QA 得到一个固定的测试对象,即便开发在 main 上继续。
在 GitFlow 中,QA 通常跟踪发布分支。陷阱在于发布分支在 QA 开始后继续变化。一旦 QA 开始,务必把发布分支当作一份合同:只接受经过批准的修复,并且通过明确流程进入。
一组小规则能让两种方法都更可预测:
- 仅从通过的构建部署到 QA。
- 把已部署的版本固定(标签、构建号或发布分支头)并通知团队。
- 限制 QA 接受的变更为 bug 修复,而非新功能。
- 按计划重置测试数据并记录哪些数据会被清理。
- 将配置作为代码管理(变量与模板),减少环境漂移。
如果你的团队使用 AppMaster 的部分输出,遵循同样原则:为 QA 重新生成并部署特定构建,而不是部署一直变化的编辑集。
当多个团队共用 QA 时
当两个团队需要不同版本时,一个共享 QA 环境会变成瓶颈。如果你无法承担多个 QA 环境,增加一个轻量的预约规则:在某个时间窗口内一个团队拥有 QA,其他人使用 dev 或 staging。功能开关也有帮助,因为未完成的工作可以部署而不对测试人员可见。
举例:团队 A 在验证本周的发布候选,因此 QA 固定为构建 1842,直到签核通过。团队 B 可以继续合并修复,但这些变更会等待下一个候选,而不是在测试周期中改变 QA 的测试对象。
逐步操作:选择一个团队每周都能遵循的工作流
把“每周发布”对你们来说意味着什么写下来。选定一个发布日和时间,决定可接受的风险级别(例如“没有已知 P1 Bug”),并记录团队规模和时区。这会把分支争论转为优先级讨论,避免无休止争执。
选择一个基础模型并在一个月内坚持执行。不要在第一天就混合模型——混合通常会增加规则却不能减少意外。
一个可调整的简单每周工作流:
- 保持分支寿命短(几小时到 1–2 天),并至少每天合并一次。
- 添加安全护栏:快速 CI、简短的必要审查,以及一小套能捕获真实破坏的自动化测试。
- 决定如何控制范围:功能开关、接近周末时的短期发布分支,或用标签锁定确切的发布提交。
- 定义热修步骤:谁可以触发、需要哪些检查、以及修复如何回到主线。
- 保持 QA 稳定:决定 QA 跟踪什么(发布分支或固定的候选),并且在测试中不要中途更改,除非重启测试周期。
把最小流程写在一页纸上。新成员应该无需会议就能按它执行。若团队一部分采用可视化工具(例如 AppMaster),另一部分用代码,这点更重要,因为交接会在规则只存在于个人脑海时失败。
示例:两种模型下的现实每周发布
想象一个 6 人的产品团队,每周五发布。两个 QA 测试人员共用一个 staging 环境,所以如果 staging 不稳定,测试对所有人都会停止。
一个繁忙的周(GitFlow)
周一:三名开发完成特性并向 develop 发起 PR。QA 开始测试由 develop 构建的 staging。
周三:团队切出 release/1.8 来保护周五的发布。新工作继续在 develop 上落地,但 QA 现在专注于 release 构建。
周四下午:出现一个晚期 bug。修复先在 release/1.8 上提交以便 QA 快速回归,然后有人把该修复合并回 develop,这通常是容易出错的环节。
典型节奏:
- 周一到周二:功能合并到
develop,staging 频繁变化。 - 周三:创建
release/1.8,staging 切换到 release 构建。 - 周四:在
release/1.8上修复 bug,然后合并回develop。 - 周五:将
release/1.8合并到main,打 tag 并部署。
同样的一周(主干开发)
这周围绕着小而频繁的合并到 main 展开。功能通过功能开关隐藏,未完成的工作可以合并而不影响用户。
周一至周四:开发者每天合并小变更。QA 测试一个固定的候选构建。
周二:出现生产问题。热修从 main 分支切出短期分支,审查后立即合并并推进到生产。因为 main 是事实来源,没有额外的回合并步骤。
无论哪种方式,团队都需要明确的晋升规则:
- staging 运行最新的固定候选构建,而不是每次新提交。
- QA 在准备好时请求一个新候选,而不是自动拉取。
- 只有针对周五发布的修复可以更改候选构建。
- 其他的变更要么通过功能开关,要么留到下一候选。
导致来回折腾和不稳定 QA 的常见错误
大多数团队失败并不是因为选错了模型,而是日常习惯与模型不匹配,导致 QA 嘈杂且发布感觉随机。
一个常见问题是让分支存在数天因为“还没准备好”。代码与 main 漂移,冲突堆积,QA 最终测试的是没人能复现的老旧与新工作混合体。
另一个问题是使用 GitFlow 但没有真正冻结。发布分支本应用于稳定,但团队不断偷偷塞入“再加一个”变更。那会把发布分支变成第二个主线,没人知道 QA 在签核什么。
当 QA 被当成半成品的垃圾场时,它也会变得不稳定。如果每次提交都进入 QA 而没有规则,测试人员就会花时间追逐移动目标,而不是验证发布。
制造最多折腾的错误包括:
- 长期存在的功能分支在后期合并并破坏无关工作。
- 发布分支仍然接受新功能。
- 没有明确的晋升路径,QA 测试的构建不是最终发布的构建。
- 热修匆忙完成却没有及时合并回所有分支。
- 环境无人负责、无明确用途且没有重置计划。
保持一套人人都能复述的规则:
- QA 只测试被固定的候选构建。
- 你从 QA 到生产推广的是相同的工件。
- 每个环境都有负责人和重置周期。
- 热修当天就把变更回流到主线。
每周发布不出意外的快速清单
当团队能自信回答几个枯燥的问题时,每周发布就能正常工作。如果你对两项或以上的关键问题回答“否”,就准备好面对最后时刻的修复和 QA 的折返。
- 是否有一个分支可以安全部署今天的版本?它应该能构建、通过冒烟测试,并避免半连接的改动。
- PR 是否保持小而在一两天内合并?长期存在的 PR 通常意味着过时的反馈和更大的冲突。
- QA 是否测试一个固定的构建,而不是一个移动的目标?QA 应该验证具体的构建号或发布候选。
- 能否把未完成的工作排除在发布之外不引发争议?功能开关、配置切换或明确的范围裁剪规则。
- 是否有人能在不凭经验的情况下执行热修和回滚?一个简短的运行手册胜过部落知识。
如果只想要一个可衡量的目标:把 QA 固定到某个发布候选并在该候选上只做有意的修复。
下一步:下周尝试一项改进
如果团队在主干开发和 GitFlow 之间僵持,不要一次性改造全部。挑出最耗时间的问题,为下一次发布做一个小实验。
如果合并冲突是最大痛点,立即缩短分支寿命。目标是每天(或隔天)落地,必要时使用功能开关。
如果 QA 不稳定是最大痛点,先从固定 QA 测试对象并定义简单的晋升步骤开始。
一个轻量试点:
- 选一个仓库和一支团队。
- 设定分支年龄上限(例如不允许分支存在超过 2 天)。
- 把 QA 固定到一个构建,并且只有通过显式晋升才能改变它。
- 跟踪三项指标:合并解决时间、QA 返工小时数和热修到生产的时间。
- 2–4 周后回顾并调整。
如果你想降低内部工具或客户门户的发布压力,像 AppMaster(appmaster.io)这样的无代码平台也能通过从可视化设计生成生产就绪的后端、网页和原生移动应用来提供帮助。但同样需要上述习惯:小变更、固定 QA 候选和明确的晋升路径。
常见问题
面向主干(trunk-based development)通常更适合每周发布,因为它促使团队频繁进行小规模合并并保持 main 可发布。GitFlow 也能做到,但它常常在需要测试和稳定的时刻引入更大的合并事件。
简单地说:面向主干意味着大多数工作通过短期分支迅速合并回 main,未完成的行为用功能开关隐藏。GitFlow 则使用更长期的分支(比如 develop)和 release/* 分支来稳定发布,因此集成分散在更多合并步骤中。
主要原因是频繁集成:更小的 PR、更小的 diff,冲突更早出现且上下文仍新鲜。代价是需要纪律性:保持 main 绿色、可靠的 CI 和增量式变更。
因为发布分支和长期存在的功能分支会产生漂移,冲突积累,然后在 feature->develop、develop->release/*、release/*->main 那些合并点集中爆发。对于每周发布,这些合并往往出现在稳定窗口,造成压力和混乱。
把 PR 做小、至少每天合并一次,并定期拉取上游变化以避免漂移。如果某个文件总是热点,指定归属或 pairing 协作胜过并行修改造成的碰撞。
把 QA 固定到一个特定的候选构建(带标签的构建号或短期的 release-candidate 分支),且不要在测试过程中随意更换它。这样 QA 就不会追着变化跑,bug 报告也能被复现,因为每个人都知道正在测试的是哪个确切构建。
使用功能开关或类似机制,让代码可以合并但默认不启用未完成的行为。这样代码可以交付到 main,而不会在用户端暴露不完整的功能,从而保持每周发布的节奏。
从生产确切的提交分支出发,做最小改动并尽快通过信任的检查部署,然后立即把修复合并回所有长期分支(例如 develop 和任何 release/*),避免同样的 bug 再次出现。
面向主干的常见失误是让半完成的工作使 main 失去可发布性,除非功能开关确实默认关闭,否则热修验证会有风险。GitFlow 的常见错误是忘记把热修合并回 develop(或 release/*),结果修复在下一次发布时又回归。
是的,只要把 AppMaster 的输出当作其他构建产物一样对待:固定 QA 测试的构建,沿用相同的候选构建向生产推进,并避免部署随机的进行中更改。关键是明确何时重新生成并部署,以及谁负责这些步骤(保留 appmaster.io 的输出和产品名不变)。


