尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
一文搞懂软件开发里的CI技术:持续集成到底是个啥?为啥都在用?
开篇一次合并周一个 6 人的游戏项目组做一个新版本。大家分头开工各开各的分支张三 → feature/新战斗系统 改了 87 个文件 李四 → feature/公会玩法 改了 64 个文件 王五 → feature/新手引导重做 改了 31 个文件 赵六 → feature/UI框架升级 ★改了 212 个文件 钱七 → feature/服务器协议重构 ★改了 105 个文件 孙八 → feature/商城 改了 43 个文件三周后说好的合并日到了。合并日当天09:00 张三先合。顺利10 分钟搞定 09:20 李四合。和张三冲突 6 个文件花了 40 分钟 10:30 王五合。冲突 14 个文件。开始需要叫人一起看 14:00 赵六合 UI 框架升级 ↓ 和前面三个人全都冲突 因为他重命名了 40 多个基类 ↓ 前面三个人的代码全部编译不过┌──────────────────────────────────────────────┐ │ 15:30 项目第一次编译失败 │ │ 16:00 张三、李四、王五被叫回来一起改 │ │ 18:00 勉强编译通过了 │ │ 18:10 启动游戏 —— 点开始直接崩溃 │ │ 没人知道是谁的问题 │ └──────────────────────────────────────────────┘接下来的五天Day 1 查崩溃。最后发现是钱七改了协议字段 但李四的公会玩法还在用旧字段 Day 2 修好了。又发现新手引导跑不通 因为赵六的 UI 框架把某个接口改了名 Day 3 修好了。战斗数值全乱 查了一天发现是两个人各自改了同一份配置表 Day 4 修好了。开始回归测试发现一堆之前好好的功能坏了 Day 5 终于能跑了┌──────────────────────────────────────────────┐ │ 6 个人 × 5 天 30 人日 │ │ 全部花在让代码能一起跑上 │ │ 一行新功能都没写 │ └──────────────────────────────────────────────┘复盘会上主程说了一句话“问题不是我们代码写得烂。问题是我们让这些代码分开跑了三周。”第一部分先理解集成这两个字一、集成 把大家的代码合到一起并且让它真的能跑很多人卡在集成这个词上觉得它很抽象。其实它就是两件事① 合并代码 git merge ② 确认合完之后还能正常工作 能编译、能跑、功能没坏第 ② 件事才是重点。代码合进去不难git merge一下就行。难的是——合进去之后它还是不是一个能用的东西二、为什么分开越久合并越痛这里有个关键的直觉合并的代价不是线性增长的是指数增长的。两个人分开写代码 ↓ 就像两条从同一点出发、慢慢分叉的路 分开 1 天 → 两条路差一点点走回去很容易 ┌─ ─────┤ └─ 分开 1 周 → 差距明显了 ┌─────── ─────┤ └─────── 分开 3 周 → 已经是两个不同的地方了 ┌────────────────────── ─────┤ └──────────────────────更糟的是人不止两个。2 个人合并 → 1 对组合 6 个人合并 → 15 对组合 ↓ 每一对都可能冲突 而且 A 和 B 合完之后的结果 可能又和 C 冲突这是第二轮冲突三、还有更隐蔽的一种冲突Git 会告诉你这两行代码冲突了你能看见。但有一类冲突Git 完全看不出来张三写的 一个函数 getPlayerLevel()返回 int 钱七写的 把 Player 类的 level 字段改成了字符串 ↓ 两个人改的是不同文件 ↓ Git 说没有冲突合并成功 ✓ ↓ 编译不过 / 或者编译过了但运行时崩溃┌──────────────────────────────────────────────┐ │ 这叫语义冲突Semantic Conflict │ │ │ │ 文本上不冲突逻辑上冲突 │ │ ↓ │ │ ★ 只有真的编译一次、跑一次才能发现 │ └──────────────────────────────────────────────┘这就是为什么合并不等于集成。合并只是把文字拼在一起集成是要确认拼出来的东西还能用。第二部分CI 是什么四、把名字拆开看Continuous Integration 持续 集成 ↓ ↓ 一直在做 合代码 确认能跑合起来的意思就是不要攒着每天甚至每小时都把代码合到一起并且每次合完都立刻自动验证一遍能不能跑。五、一个最笨但最准确的比喻没有 CI 攒一个月的碗月底一次性洗 ↓ 堆成山、发霉、粘在一起、洗一整天 有 CI 每顿饭后立刻洗 ↓ 每次 3 分钟永远不会堆积注意重点洗碗的总量没变但总耗时差了十倍。因为攒起来之后你多出来的工作是把粘在一起的碗分开、刮掉干掉的污渍、重新排序。这些工作当天洗根本不存在。代码合并一模一样。六、CI 每次具体干三件事┌──────────────────────────────────────────────┐ │ 你 push 了一次代码 │ │ ↓ │ │ ① 拉取最新主干 合并你的改动 │ │ ↓ │ │ ② 构建编译 / 打包 │ │ → 编译不过立刻停告诉你 │ │ ↓ │ │ ③ 验证跑测试 / 代码检查 │ │ → 测试挂了立刻停告诉你 │ │ ↓ │ │ ④ 全绿 → 通知没问题 │ │ 任何一步红 → 5 分钟内 你 │ └──────────────────────────────────────────────┘就这么简单。CI 的本质就是一个自动化的看门人。七、澄清一个巨大的误解❌ “我们买了 Jenkins / 用了 GitHub Actions所以我们有 CI 了。”这是最普遍的错误认知。CI 首先是一种【工作方式】 ↓ 工具只是用来支撑这种工作方式的判断你到底有没有在做 CI只看一个问题你的团队成员平均多久把自己的代码合进主干一次每天至少一次 → ✅ 这是 CI 三五天一次 → ⚠️ 勉强算 一两周一次 → ❌ 你只是有个构建服务器 只在版本末期合 → ❌ 那叫集成地狱和 CI 相反┌──────────────────────────────────────────────┐ │ 一个团队可以 │ │ 有 Jenkins但没有 CI │ │ 因为大家还是开长期分支月底才合 │ │ │ │ 没有 Jenkins但有 CI │ │ 每天合合完手动编译跑一遍也算 │ │ ↓ │ │ 当然有工具会让这件事轻松一百倍 │ └──────────────────────────────────────────────┘第三部分为什么都在用八、价值一把找 bug变成找不到都难这是 CI 最大、但最少被说清楚的价值。想象你在草堆里找针场景 A没有 CI 三周攒了 2000 次提交一次性合并 ↓ 发现有个 bug ↓ 嫌疑范围2000 次提交、6 个人、几万行改动 ↓ ★ 排查成本几小时到几天场景 B有 CI 每次提交都自动验证 ↓ 第 1487 次提交时CI 亮红灯 ↓ 嫌疑范围★ 就是这一次提交通常几十行 ↓ ★ 排查成本几分钟┌──────────────────────────────────────────────┐ │ CI 的核心魔法 │ │ │ │ 把出错了和是哪行出的错 │ │ 这两件事的距离压缩到最小 │ │ │ │ 不是它更会找 bug │ │ 是它让 bug 藏不住 │ └──────────────────────────────────────────────┘一个数字bug 发现得越晚修复成本越高发现时机 相对修复成本 ────────────────────────────── 写代码时 1x CI 上几分钟后 ~2x QA 测试时 ~10x 上线后 ~50x为什么差这么多写代码时发现 → 你脑子里还记得这段逻辑改 2 分钟 上线后发现 → 你已经忘了这段代码 → 要先复现问题 → 要定位是哪次改动引入的 → 要评估影响范围 → 要紧急发版 → 要通知用户、可能要补偿CI 做的事就是把所有问题尽可能往左推。九、价值二主干永远是能用的没有 CI 的项目主干是薛定谔的现在的主干能跑吗 不知道你自己拉下来试试这句话带来的连锁反应主干状态不确定 ↓ 新人拉代码编译不过卡半天 ← 入职第一天就被劝退 ↓ 策划想看个功能拉不到能跑的包 ↓ QA 不知道该测哪个版本 ↓ 要演示了临时抓一个应该能跑的版本现场崩溃有 CI 之后主干上每一次提交都被验证过 ↓ ★ 主干随时可构建、可运行变成一个可以依赖的承诺 ↓ 任何人、任何时候拉主干都能跑起来这个承诺的价值比很多人想的大得多——它是随时能出包的前提而随时能出包是快速迭代的前提。十、价值三把人肉规矩变成机器规矩每个团队都有一堆口头约定提交前记得跑一下编译 记得跑单元测试 代码风格要统一 不要提交 .meta 冲突的文件 不要把调试代码提交上去这些约定的共同问题靠自觉而自觉会失效。赶进度的时候会忘 新人不知道有这规矩 老人觉得我这次改动很小不用测CI 把这些变成硬性检查┌──────────────────────────────────────────────┐ │ CI 流水线里加一步 │ │ 代码风格检查不过 → 直接红灯合不进去 │ │ ↓ │ │ 规矩从希望你遵守 │ │ 变成你不遵守就进不来 │ └──────────────────────────────────────────────┘而且它不会疲劳、不会给面子、不会因为你是主程就放行。十一、价值四敢重构了这是个容易被忽略的心理层面的价值。没有测试和 CI 的项目 ↓ 这段代码写得很烂但我不敢改 改了万一哪里坏了我不知道 ↓ 屎山越堆越高有 CI 测试覆盖 ↓ 我改一下CI 会告诉我有没有坏 ↓ ★ 敢改了 ↓ 代码质量能持续改善而不是单向腐化第四部分一条 CI 流水线长什么样十二、完整流程┌──────────────────────────────────────────────────────┐ │ 开发者 │ │ git push / 提 Pull Request │ └───────────────────┬──────────────────────────────────┘ ↓ webhook 触发 ┌──────────────────────────────────────────────────────┐ │ ① 准备环境1~2 分钟 │ │ · 分配一台干净的机器 / 容器 │ │ · 拉代码含子模块、LFS 大文件 │ │ · 恢复缓存依赖包、编译中间产物 │ └───────────────────┬──────────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────────┐ │ ② 静态检查30 秒 ~ 2 分钟★ 最快放最前面 │ │ · 代码风格lint / format │ │ · 静态分析找明显的空指针、死代码 │ │ · 敏感信息扫描有没有把密钥提交上来★ │ │ · 依赖漏洞扫描 │ └───────────────────┬──────────────────────────────────┘ ↓ 通过 ┌──────────────────────────────────────────────────────┐ │ ③ 构建2~30 分钟看项目 │ │ · 编译 │ │ · 打包 │ │ ★ 这一步失败最常见也最致命 │ └───────────────────┬──────────────────────────────────┘ ↓ 通过 ┌──────────────────────────────────────────────────────┐ │ ④ 单元测试1~10 分钟 │ │ · 测单个函数/类的逻辑 │ │ · 不依赖数据库、网络 │ │ · 应该很快 │ └───────────────────┬──────────────────────────────────┘ ↓ 通过 ┌──────────────────────────────────────────────────────┐ │ ⑤ 集成测试5~30 分钟 │ │ · 起数据库、起服务测模块之间的协作 │ │ · 慢可以只在合主干时跑 │ └───────────────────┬──────────────────────────────────┘ ↓ 通过 ┌──────────────────────────────────────────────────────┐ │ ⑥ 产出物Artifact │ │ · 保存构建出来的包 │ │ · 上传到内部分发平台 / TestFlight / 测试服 │ └───────────────────┬──────────────────────────────────┘ ↓ ┌──────────────────────────────────────────────────────┐ │ ⑦ 通知 │ │ · 企业微信 / Slack / 钉钉 │ │ · ★ 失败必须 到具体的人不能只发群里 │ └──────────────────────────────────────────────────────┘十三、顺序的讲究快的放前面┌──────────────────────────────────────────────┐ │ 为什么静态检查要放在编译前面 │ │ │ │ 因为它只要 30 秒 │ │ 而编译要 10 分钟 │ │ ↓ │ │ 如果你只是少了个分号、格式没对齐 │ │ 30 秒就能告诉你不用等 10 分钟 │ └──────────────────────────────────────────────┘这叫「快速失败」Fail Fast 原则越便宜的检查越靠前 越可能失败的检查越靠前第五部分实际配置长什么样十四、一个最小的 CI10 行别被吓到CI 可以很简单。这是一个后端 Java 项目的最小配置# .github/workflows/ci.ymlname:CIon:[push,pull_request]# 每次推代码、每次提 PR 都跑jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4# 拉代码-uses:actions/setup-javav4# 装 JDKwith:{java-version:17,distribution:temurin}-run:mvn-B verify# 编译 跑测试就这样。你已经有 CI 了。它会做的事每次有人 push自动拉代码、编译、跑测试失败时在 GitHub 上打个红叉PR 页面会显示检查未通过十五、一个完整一些的例子name:CIon:push:branches:[main,develop]pull_request:branches:[main,develop]# ★ 同一个分支有新提交时取消上一次还在跑的concurrency:group:${{github.workflow}}-${{github.ref}}cancel-in-progress:truejobs:# ═══════ 阶段一快速检查并行1~2 分钟═══════lint:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-nodev4with:{node-version:20,cache:npm}-run:npm ci-run:npm run lint# 代码风格-run:npm run typecheck# 类型检查secret-scan:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4with:{fetch-depth:0}-name:扫描是否误提交了密钥uses:gitleaks/gitleaks-actionv2# ═══════ 阶段二测试 ═══════test:needs:[lint]# ★ lint 过了才跑runs-on:ubuntu-latestservices:# CI 里临时起的依赖postgres:image:postgres:16env:POSTGRES_PASSWORD:testoptions:---health-cmd pg_isready--health-interval 10s--health-retries 5redis:image:redis:7steps:-uses:actions/checkoutv4-uses:actions/setup-nodev4with:{node-version:20,cache:npm}-run:npm ci-run:npm run test:unit-run:npm run test:integrationenv:DATABASE_URL:postgres://postgres:testlocalhost:5432/testREDIS_URL:redis://localhost:6379-name:上传测试报告if:always()# ★ 失败时也要上传uses:actions/upload-artifactv4with:name:test-reportpath:coverage/# ═══════ 阶段三构建 ═══════build:needs:[test,secret-scan]runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-uses:actions/setup-nodev4with:{node-version:20,cache:npm}-run:npm ci-run:npm run build-name:保存构建产物uses:actions/upload-artifactv4with:name:dist-${{github.sha}}path:dist/retention-days:7# ═══════ 通知 ═══════notify:needs:[build]if:failure()# ★ 只在失败时通知runs-on:ubuntu-lateststeps:-name:通知到群run:|curl -s -X POST ${{ secrets.WEBHOOK }} \ -H Content-Type: application/json \ -d { msgtype: text, text: { content: ❌ CI 失败\n分支: ${{ github.ref_name }}\n提交者: ${{ github.actor }}\n链接: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}, mentioned_list: [${{ github.actor }}] } }十六、游戏项目的 CI 有什么不一样因为前面几篇都在聊游戏这里单独说一下。┌──────────────────────────────────────────────────────┐ │ 游戏项目做 CI 的三个特殊困难 │ ├──────────────────────────────────────────────────────┤ │ ① 资源巨大 │ │ 美术资源几十上百 GB拉一次代码就要很久 │ │ → 必须用 Git LFS并且做好缓存 │ ├──────────────────────────────────────────────────────┤ │ ② 构建极慢 │ │ Unity 打一个 iOS 包可能要 40 分钟起 │ │ → 必须分层不是每次提交都打完整包 │ ├──────────────────────────────────────────────────────┤ │ ③ 需要特定机器 │ │ iOS 必须用 Mac而 Mac 构建机很贵 │ │ → 通常用自建 Mac mini 做 self-hosted runner │ ├──────────────────────────────────────────────────────┤ │ ④ 单元测试难写 │ │ 大量逻辑和引擎、表现耦合 │ │ → 先从能编译开始别一上来就追求测试覆盖率 │ └──────────────────────────────────────────────────────┘分层策略关键┌──────────────────────────────────────────────────────┐ │ 每次提交要快5 分钟内 │ │ · 代码风格检查 │ │ · 编译 Editor★ 不打包只验证能不能编译 │ │ · 跑纯逻辑的单元测试 │ │ · 检查资源命名规范、是否有超大贴图 │ ├──────────────────────────────────────────────────────┤ │ 合并到 develop可以慢一点30 分钟 │ │ · 完整编译 │ │ · 打一个 Android 测试包 │ │ · 跑集成测试 │ ├──────────────────────────────────────────────────────┤ │ 每晚定时Nightly可以很慢2 小时 │ │ · 打 iOS Android 完整包 │ │ · 上传 TestFlight / 内部分发 │ │ · 跑自动化冒烟测试启动、进主城、打一关 │ │ · 性能基线检查包体大小、启动耗时、内存峰值 │ └──────────────────────────────────────────────────────┘★ 核心原则 开发者每次提交要等的时间必须短到他愿意等 慢的东西全部推到合并时和每晚第六部分CI 和 CD 的关系十七、三个词的区别这三个词经常被混着说但含义不同┌──────────────────────────────────────────────────────┐ │ CI 持续集成 Continuous Integration │ │ 频繁合代码 自动验证能不能跑 │ │ 终点产出一个验证过的包 │ ├──────────────────────────────────────────────────────┤ │ CD 持续交付 Continuous Delivery │ │ 在 CI 基础上自动把包部署到测试/预发环境 │ │ ★ 随时可以上线但上不上线由人决定 │ │ 终点一个随时能上线的包躺在那等人点按钮 │ ├──────────────────────────────────────────────────────┤ │ CD 持续部署 Continuous Deployment │ │ 比上面更进一步连按钮都不用点 │ │ 验证全过 → 自动上生产 │ │ 终点代码合并后几分钟用户就用上了 │ └──────────────────────────────────────────────────────┘代码提交 ↓ [═══ CI ═══] 编译 测试 产包 ↓ [═ 持续交付 ═] 自动部署到测试环境、预发环境 ↓ ★ 人工审批 ←── 持续交付停在这 ↓ [═ 持续部署 ═] 自动上生产 ←── 持续部署不停十八、该做到哪一步┌──────────────────────────────────────────────────────┐ │ 几乎所有团队都应该做 CI │ │ 成本低收益立竿见影 │ ├──────────────────────────────────────────────────────┤ │ 大部分团队应该做到持续交付 │ │ 能自动部署到测试环境就很够用了 │ ├──────────────────────────────────────────────────────┤ │ 持续部署看业务性质 │ │ Web 服务 / 后端 API → 很适合 │ │ 移动 App → ★ 做不到要过应用商店审核 │ │ 游戏客户端 → 不适合版本要配合运营节奏 │ │ 金融/医疗 → 通常有合规要求需人工审批 │ └──────────────────────────────────────────────────────┘所以对游戏团队来说通常是CI必做 自动出包分发到测试渠道持续交付 正式发版还是人工控制第七部分从零开始怎么落地十九、四个阶段别想一步到位阶段 1先让它跑起来第 1 周目标每次 push自动编译一次 编译失败群里有通知 不需要单元测试、覆盖率、复杂流水线┌──────────────────────────────────────────────┐ │ ★ 这一步的价值已经很大了 │ │ │ │ 主干编译不过这个问题 │ │ 在很多团队占了所有集成问题的一半以上 │ └──────────────────────────────────────────────┘阶段 2加上最基本的验证第 2~4 周□ 代码风格检查lint □ 密钥泄露扫描★ 强烈建议成本极低收益极大 □ 给核心的纯逻辑模块补几个单元测试 不要追覆盖率先挑最容易出错、最重要的写阶段 3改变工作方式第 2~3 个月★ 最难的一步这一步不是技术问题是习惯问题。□ 分支存活时间 ≤ 2 天 不是不许开分支是开了就快点合回去 □ 每个人每天至少合一次主干 □ 主干保护 · 必须通过 CI 才能合并 · 必须至少一人 review □ ★ 建立红灯停规则 CI 红了全组优先修它不合新代码红灯停是 CI 能不能活下来的分水岭。如果 CI 红了大家照常提交 ↓ 红灯会一直红下去 ↓ 大家开始习惯性忽略红灯 ↓ ★ CI 就死了 —— 它还在跑但没人看了这是 CI 最常见的死法。不是被关掉是被无视。阶段 4优化和扩展持续□ 优化构建速度缓存、并行、增量 □ 加集成测试、自动化冒烟测试 □ 自动出包分发TestFlight / 蒲公英 / 内部平台 □ 加性能基线检查 □ 数据化统计失败率、平均修复时间二十、工具怎么选工具适合特点GitHub Actions代码在 GitHub配置简单生态最好公开仓库免费GitLab CI代码在 GitLab和仓库深度集成可自建Jenkins复杂 / 特殊需求最灵活插件最多但要自己维护TeamCity大型项目功能强游戏行业用得多CircleCI / Travis开源项目配置简单各云厂商的 CI已在用该云和云服务集成方便★ 选择建议 代码托管在哪就先用哪家自带的 别一上来就自建 Jenkins —— 维护成本比你想的高关于构建机云上托管的 runner ✓ 省事不用维护 ✗ 大项目慢每次都是干净环境缓存要重下 ✗ iOS/Mac 机器贵 自建 runnerself-hosted ✓ 快本地缓存、本地资源 ✓ 游戏项目基本必须自建Unity 授权、Mac 机器 ✗ 要自己维护、要保证环境一致第八部分构建速度是生死线二十一、为什么速度这么重要CI 跑一次要 5 分钟 ↓ 开发者提交完喝口水就好了 → 愿意频繁提交 ✓ CI 跑一次要 40 分钟 ↓ 开发者提交一次要等大半天那我攒一攒再提 ↓ ★ 回到了攒着不合的老路 ↓ CI 名存实亡┌──────────────────────────────────────────────┐ │ CI 的速度直接决定了大家的提交频率 │ │ 而提交频率就是 CI 的全部价值所在 │ │ ↓ │ │ 慢的 CI 会自己把自己杀死 │ └──────────────────────────────────────────────┘一个参考线理想 5 分钟 开发者会等着看结果 可接受 10 分钟 去做点别的回来看 危险 20 分钟 开始有人绕过它二十二、加速的几个办法┌──────────────────────────────────────────────────────┐ │ ① 缓存依赖 │ │ npm / maven / gradle / NuGet 包缓存 │ │ Unity 的 Library 目录缓存 │ │ ★ 最容易见效的一招经常能省一半时间 │ ├──────────────────────────────────────────────────────┤ │ ② 并行 │ │ lint、单测、类型检查同时跑不排队 │ ├──────────────────────────────────────────────────────┤ │ ③ 分层前面讲过的 │ │ 快的每次跑慢的只在合并时跑最慢的每晚跑 │ ├──────────────────────────────────────────────────────┤ │ ④ 只跑受影响的部分 │ │ 改了哪个模块就只测哪个模块 │ │ 大仓库 / monorepo 必备 │ ├──────────────────────────────────────────────────────┤ │ ⑤ 自动取消过期任务 │ │ 同一分支连续推三次前两次直接取消 │ │ 前面配置里的 cancel-in-progress │ ├──────────────────────────────────────────────────────┤ │ ⑥ 更好的机器 │ │ ★ 最简单粗暴但最有效的一招 │ │ 一台构建机的钱 vs 10 个人每天多等 20 分钟 │ │ 算一下就知道哪个贵 │ └──────────────────────────────────────────────────────┘第九部分怎么知道 CI 做得好不好二十三、四个关键指标┌──────────────────────────────────────────────────────┐ │ ① 主干构建成功率 │ │ 目标 90% │ │ 太低 → 说明大家提交前根本没自测 │ │ ⚠️ 但也不能是 100%100% 说明 CI 检查得太松 │ ├──────────────────────────────────────────────────────┤ │ ② ★ 平均修复时间红灯持续多久 │ │ 目标 30 分钟 │ │ 这是最能反映团队 CI 文化的指标 │ │ 红灯挂一整天 没人把它当回事 │ ├──────────────────────────────────────────────────────┤ │ ③ 流水线时长 │ │ 目标主流水线 10 分钟 │ │ 超过 20 分钟就要专门花时间优化 │ ├──────────────────────────────────────────────────────┤ │ ④ ★ 人均每日合并次数 │ │ 目标≥ 1 次/人/天 │ │ 这个数字直接反映你到底有没有在做 CI │ └──────────────────────────────────────────────────────┘二十四、一个容易被忽略的健康指标「不稳定测试」Flaky Test的比例 同样的代码有时过有时不过的测试┌──────────────────────────────────────────────┐ │ 不稳定测试是 CI 的慢性毒药 │ │ │ │ 第 1 次红灯 → 重跑一下绿了 → 哦误报 │ │ 第 5 次红灯 → 直接重跑都不看 │ │ 第 20 次 → ★ 真的挂了也以为是误报 │ │ ↓ │ │ CI 失去了可信度等于没有 │ └──────────────────────────────────────────────┘处理原则 发现不稳定测试 → 立刻修或者立刻标记跳过 ★ 绝不允许重跑一下就好了成为常态第十部分坑表#坑后果解法1以为装了工具就是 CI大家还是开长期分支问题照旧CI 首先是频繁合并这个行为2分支活太久合并时依然是地狱分支寿命 ≤ 2 天3红灯不修继续提交CI 永久变红最终被无视建立红灯停规则4CI 太慢20 分钟大家攒着提交CI 失效缓存 并行 分层5不稳定测试放任不管CI 失去可信度立刻修或立刻跳过6失败通知只发群里不 人没人认领红灯挂一天精确 到提交者7通知太多成功也发大家屏蔽了通知只通知失败 从红转绿8本地能过 CI 过不了反复试错浪费大量时间环境容器化本地可复现9没有主干保护CI 红着也能合进去设置必须通过检查才能合并10一上来就追求测试覆盖率投入巨大团队抵触半途而废先做到能编译再逐步加11测试依赖外部服务对方挂了 CI 就红误报用 mock / 容器起本地依赖12测试之间互相影响单跑过、一起跑就挂每个测试独立、可任意顺序13密钥硬编码在配置里泄露风险用 Secrets加密钥扫描14构建产物不保留出问题无法回溯保存 artifact设保留期15日志不清晰红了不知道为啥红失败时输出足够上下文16构建机环境手工配置换机器就跑不起来环境即代码Docker / 脚本17多人共用一台构建机串行排队排队比构建还久加机器 / 加并发数18游戏项目每次提交都打完整包40 分钟一次没人受得了分层提交只编译夜里才打包19没做 Git LFS 缓存每次拉几十 G 资源配置 LFS 缓存20只有 CI 没有主干可运行的承诺主干还是不能跑CI 通过 主干可用要当真21新人上手没文档不知道怎么看、怎么修写一页CI 红了怎么办22把 CI 当成 QA 的替代品上线后照样出事CI 只能测已知会坏的不替代测试23流水线配置只有一个人懂那人休假就瘫痪配置进仓库 有人备份24不统计任何指标不知道 CI 是好是坏无法改进至少统计成功率和修复时间速查表一句话定义CI 每天至少把代码合进主干一次 每次合并都自动验证一遍能不能跑 一旦失败立刻修判断你有没有在做 CI只问一个问题 ★ 团队成员平均多久合一次主干 每天 ≥ 1 次 → ✅ 是 CI 一周一次 → ❌ 只是有个构建服务器 版本末期才合 → ❌ 集成地狱CI 每次做的三件事① 拉最新代码 合并 ② 构建能不能编译 ③ 验证测试 / 检查 → 任何一步失败立刻通知具体的人流水线顺序快的在前静态检查30s→ 构建5m→ 单测3m→ 集成测试15m→ 产包四条铁律① 频繁合并 分支寿命 ≤ 2 天 ② 每次提交都跑 不是每天跑一次 ③ 快 主流水线 10 分钟 ④ ★ 红灯停 CI 红了全组优先修四个指标主干构建成功率 90% ★ 平均修复时间 30 分钟 流水线时长 10 分钟 ★ 人均日合并次数 ≥ 1 次CI / 持续交付 / 持续部署CI 合代码 验证 → 产出一个验证过的包 持续交付 自动部署测试环境 → 随时能上线等人点按钮 持续部署 自动上生产 → 合并后几分钟用户就用上了 移动 App / 游戏客户端做到持续交付即可 正式发版必须人工控制落地四阶段① 第 1 周 push → 自动编译 → 失败通知 就这么简单 ② 第 2~4 周 lint 密钥扫描 核心单测 ③ 第 2~3 月 ★ 改工作方式短分支 每天合 红灯停 ④ 持续 优化速度、加自动化测试、自动出包游戏项目分层策略每次提交 代码检查 编译验证 纯逻辑单测 5 分钟 合主干 完整编译 打 Android 测试包 30 分钟 每晚定时 双平台打包 上传分发 冒烟测试 性能基线加速六招缓存依赖 / 并行任务 / 分层触发 / 只测受影响模块 / 取消过期任务 / ★ 换更好的机器结语三个反直觉的真相一、CI 里最重要的字是持续不是集成。大部分团队理解 CI 的方式是“搭一套自动构建系统”。于是他们买了服务器、写了流水线、配好了通知然后继续开着两周一合的长期分支。结果 每次合并CI 就亮起一大片红灯 因为一次性合进来几百个改动 谁都不知道是哪个引起的 ↓ CI 从帮手变成了每次合并都要过的一道坎 ↓ 大家开始讨厌它CI 真正的那个技巧简单到让人不敢相信把批次改小。一次合 500 行出问题要在 500 行里找。一次合 50 行出问题只在 50 行里找。而你合 10 次 50 行总量是一样的总痛苦却小一个数量级。工具只是让合 10 次这件事变得不费力而已。没有改变合并频率就没有做 CI。二、CI 的产出不是更少的 bug是更便宜的 bug。这是个经常让人失望的真相上了 CI你的 bug 数量不会明显减少。人还是那些人代码还是那个水平该写错的还是会写错。CI 改变的是 bug 的成本结构没有 CI bug 在两周后被发现 嫌疑范围几千行、6 个人的改动 排查1 天 修复时你已经忘了当时在想什么 有 CI bug 在 6 分钟后被发现 嫌疑范围★ 你刚才那 40 行 排查2 分钟 修复时你脑子里那段逻辑还热着同样一个 bug成本差了两个数量级。所以衡量 CI 有没有用不要看bug 变少了吗要看“从出问题到定位到具体原因要多久”这个时间从一天变成五分钟就是 CI 全部的价值。三、CI 最常见的死法不是被关掉是被无视。几乎没有团队会正式宣布我们不用 CI 了。CI 的死亡过程是这样的第 1 周 CI 红了大家紧张立刻修 ✓ 第 1 个月 有个测试老是随机失败重跑一下就好 第 2 个月 重跑一下变成条件反射 第 3 个月 CI 平均要跑 25 分钟了没人优化 第 4 个月 大家提交前不看 CI 了反正也慢 第 5 个月 主干红了三天因为那个失败是误报 第 6 个月 ★ CI 还在跑服务器还在烧钱 但没有任何人看它了杀死 CI 的从来不是技术问题是三件小事① 太慢 → 大家绕过它 ② 不稳定测试 → 大家不信它 ③ 红灯不修 → 大家习惯它是红的这三件事任何一件失控CI 都会在几个月内静悄悄地变成摆设。所以真正需要持续投入的不是搭流水线那是一周的活而是· 每季度花时间把构建速度压回 10 分钟以内 · 不稳定测试当天修掉绝不让重跑成为习惯 · 红灯出现时有人真的会停下手里的活去修CI 是一套纪律工具只是纪律的载体 ↓ 纪律松了再贵的工具也救不回来
RELATED

相关推荐

新一代IDE Copilot X编程效率实测分析:TaoToken统一Key接入AI结对编程的范式革命

新一代IDE Copilot X编程效率实测分析:TaoToken统一Key接入AI结对编程的范式革命

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/28 7:31:01
OpenCV多目标追踪实战:鼠标交互与CamShift光流融合方案

OpenCV多目标追踪实战:鼠标交互与CamShift光流融合方案

简介:这份资源面向计算机视觉初学者与需要完成课程大作业的学生,围绕OpenCV与Python实现多目标追踪,重点讲解KCF算法的原理与工程落地。项目从视频读取与预处理入手,通过鼠标交互框选待追踪目标,再借助KCF循环卷积逐帧…

📅 2026/9/28 7:26:00
昇思MindSpore数据变换与Pipeline编排:大模型训练性能优化实战指南

昇思MindSpore数据变换与Pipeline编排:大模型训练性能优化实战指南

很多人第一次接触昇思 MindSpore 时,注意力都放在网络搭建、损失函数和评测指标上,真正到了跑大模型训练和微调的时候,才发现卡在数据环节的时间比调模型还多。大模型场景下数据量动辄几十上百 GB,如果 mindspore.dataset 里的数据…

📅 2026/9/28 7:26:00
MORE NEWS

更多资讯

📰

React Native异步状态更新与渲染机制全面解析

我先跟你说个特别真实的场景:RN 项目里调完setState,紧接着下一行打印this.state,结果拿到的还是旧数据。你以为是代码写错了,查了半天,发现不是 bug,是机制。状态更新是异步的,渲染是 React 自…

📰

Eclipse怎么做网页免费工具全解析:备案别花冤枉钱

Eclipse怎么做网页免费工具全解析:备案别花冤枉钱 备案流程一头雾水?很多人第一反应是找代办,结果一问多少钱,从几百到几千都有,心里没底。其实,对于用 Eclipse…

📰

浪网站制作对比评测:告别拖延,3招搞定技术选型

浪网站制作对比评测:告别拖延,3招搞定技术选型 改个按钮颜色,建站公司让你等一周?这种憋屈谁受得了? 别骂了,先看看你的网站是用什么技术堆的。很多老板不懂技术,只懂扔需求,结果被外包坑得底掉。今天咱们不整虚的,直接上硬菜,通过 对比评测…

📰

做网站需要提供什么条件?避开被黑挂马坑,选对哪家好

做网站需要提供什么条件?避开被黑挂马坑,选对哪家好 网站被黑挂马不知道怎么办?别慌,先自查。很多老板找建站公司,问“做网站需要提供什么条件”,结果只给了个Logo和几段文字,上线没三天,网站变成赌博广告,百度也搜不到,找服务商推诿,找技术不…

📰

小项目开发sop流程

文章目录从零开始做项目:一份完整的个人项目开发流程指南(以贪吃蛇为例)一、立项二、可行性分析技术可行性要分析什么?🌰 实战例子:开发一个贪吃蛇三、需求分析四、功能流程图五、产品原型图六、架构搭建为…

📰

S905L3SB盒子刷机指南:安卓9.0线刷固件+当贝桌面纯净版集成

如果你手里有一台运营商送的IPTV盒子,芯片方案是晶晨S905L3SB,那大概率你和我一样,拿到手没几天就被它自带桌面里的广告和推荐位烦得不行。开机先放十几秒广告,切个频道又弹个充值页面,想装个第三方App还被各种限制卡住…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬