3. 持续集成(CI):让代码合并不再是一场噩梦

说实话,我见过太多团队把「持续集成」挂在嘴边,结果呢?每天下午五点,所有人都不敢提交代码。为什么?因为一提交就炸,一合并就冲突。这哪是持续集成,分明是持续焦虑。

持续集成,说白了就是:让开发者的代码变更,能够频繁地、自动地合并到主干,并且每次合并都经过自动化验证。我个人的经验是,一天至少集成一次,甚至更多。你想想看,如果一周才集成一次,那中间这五天,每个人都在自己的分支上「闭门造车」,最后合并时那个酸爽……嗯,我经历过,不想再来第二次。

3.1 CI 的核心概念

CI 不是什么高深的理论,它就是一个习惯。就像你每天刷牙一样自然。核心就三点:

  • 频繁提交:别攒代码。我见过有人一周写了两千行,然后一次性 push。结果代码审查时,谁也看不懂他到底改了啥。小步提交,每次改动控制在 200 行以内,这是黄金法则。
  • 自动化构建:每次提交,服务器自动拉代码、编译、打包。别指望人工去执行这些步骤,人总会忘的。
  • 快速反馈:构建失败了?十分钟内必须通知到提交者。我曾经在项目里设过规矩:谁让构建变红超过 30 分钟,谁请全组喝咖啡。效果立竿见影。

核心原则:如果构建失败,整个团队的第一优先级就是修复它。不要在上面堆新功能,那只会让问题更复杂。

3.2 CI 工具链:选对工具,事半功倍

工具这东西,没有最好的,只有最合适的。我这些年用过不少,给你列个清单,都是实战检验过的:

环节 推荐工具 我的使用感受
版本控制 Git(GitHub / GitLab) 没得选,Git 已经是事实标准了。SVN 的时代过去了。
CI 服务器 Jenkins / GitLab CI / GitHub Actions 小团队我推荐 GitHub Actions,零运维。大企业 Jenkins 更灵活。
构建工具 Maven / Gradle / npm / pip Java 项目我习惯用 Gradle,比 Maven 快不少。
代码质量 SonarQube / ESLint / Checkstyle SonarQube 是必选项,后面会细说。
测试框架 JUnit / pytest / Jest 选语言对应的主流框架就行,别整花活。
制品仓库 Nexus / Artifactory / Docker Registry 构建产物一定要统一管理,别散落在各个开发机里。

我个人习惯是,能 SaaS 就 SaaS,能少维护就少维护。GitHub Actions 和 GitLab CI 都是云原生的,不需要你搭服务器,省心很多。但如果你在金融、军工这类行业,那 Jenkins 自建还是主流选择。

3.3 构建流水线:从代码到产物的高速公路

构建流水线,就是 CI 的骨架。我把它分成几个阶段,每个阶段都有明确的目标:

  1. 代码检出:从 Git 拉取最新代码。注意,这里要拉取的是合并后的代码,不是单纯的分支代码。
  2. 依赖安装:下载项目依赖。这一步往往是瓶颈,我建议用缓存加速。比如 Maven 的本地仓库、npm 的 cache 目录,都可以在流水线里配置持久化。
  3. 静态检查:跑代码规范、潜在 bug 检测。这一步要快,别让开发者等太久。
  4. 单元测试:跑所有单元测试,生成覆盖率报告。我要求团队覆盖率不低于 80%,低于这个数,流水线直接红。
  5. 构建打包:编译代码,生成可部署的制品(jar、war、docker image 等)。
  6. 制品归档:把构建产物上传到制品仓库,打上版本标签。

下面是一个简单的 GitHub Actions 流水线示例,你可以参考一下:

name: CI Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

    - name: Run tests and build
      run: mvn clean verify

    - name: Upload build artifact
      uses: actions/upload-artifact@v3
      with:
        name: app-jar
        path: target/*.jar

小技巧:流水线里的每一步,都应该能在本地复现。我曾经踩过坑,流水线里用的 JDK 版本和本地不一样,结果本地编译通过,流水线却挂了。排查了半天,最后发现是版本不一致。所以,环境一致性是流水线设计的第一要务。

3.4 代码质量门禁:把烂代码挡在门外

代码质量门禁,就是 CI 流水线里的「安检员」。它不会让代码「带病上线」。我见过太多项目,代码质量一塌糊涂,但功能能跑,就没人管。结果呢?维护成本越来越高,最后谁都不敢动老代码。

质量门禁通常包含这几个维度:

  • 代码规范:缩进、命名、注释等。用 ESLint、Checkstyle 这类工具自动检查。
  • 复杂度:圈复杂度超过 15 的方法,必须重构。我见过一个方法写了 300 行,圈复杂度 80 多,那代码根本没法看。
  • 重复代码:重复率超过 5% 就要报警。别复制粘贴,提取公共方法。
  • 测试覆盖率:行覆盖率不低于 80%,分支覆盖率不低于 70%。
  • 安全漏洞:依赖库里的已知漏洞,必须修复或升级。

SonarQube 是我最常用的质量门禁工具。它会把这些问题量化成一个「质量门禁状态」,通过或失败,一目了然。我曾经在一个遗留项目里引入 SonarQube,第一次扫描,代码质量评级是 E(最低级)。团队花了两个月,硬是把评级提到了 A。从那以后,新代码的质量再也没有掉下去过。

避坑指南:质量门禁的阈值不要一开始就设得太高。我曾经在一个新项目里,直接把覆盖率门槛设到 90%,结果开发人员天天为了凑覆盖率写一些无意义的测试。后来我调整策略:新代码覆盖率 80%,存量代码逐步提升。这样既保证了质量,又不打击积极性。

最后说一句,CI 不是银弹。它解决的是「集成」的问题,解决不了「需求不清晰」「设计不合理」的问题。但如果你连 CI 都没做好,那后面的一切——持续交付、持续部署——都是空中楼阁。先把 CI 跑起来,让每次代码合并都变得安全、可预期。这才是 DevOps 的第一步。