慢下来

今天是国庆假期之后新的一周,但我依然没有恢复过来。

前三天刷了两本书:《余生不上班:城市打工人的乡村冒险记》、《独家对话美的方洪波:我只是一个过客,不需要被任何人记住》。

然后在 AI 协作下,发布了第一个开源组件 upload-file v1.0.0 GA。

最后四天带家人,突击式打卡海洋馆、野生动物园、自然博物馆。

可以说假期里的每一天,节奏都远远快过日常。

或许,我应该慢下来。

向着 Forward Deployed Engineer 靠近

“慢下来”这个词,是我花了近两个月时间,去理解并尝试 FDE 之后,才慢慢品出来的。

FDE(Forward Deployed Engineer)是一种更靠近现场、更靠近决策的前线工程师角色。假定有一个原始需求,通过 AI 去实现它——这听起来有些 Vibe Coding 的味道:人负责想,机器负责写。

但故事不会一次成型。当需求实现到尾声的时候,我作为一个多变的产品经理提出了第一个需求改点(rc.1),然后开始指导 AI 去调整实现。

接着是 rc.2、rc.3……直到第九个改点(rc.9)之后,又改了一次,才最终敲定 GA 版本。

但,我真的不是产品经理。

升级依赖这件”小事”

如果说 FDE 是”慢下来”的实践,那么”升级依赖”就是我慢不下来的另一面。

我以前很怕”升级依赖”四个字。

不是因为难,是因为它永远排不进 todo list。

周一确定升级,周二本地验证发现一堆依赖报错,排查半天也没找到对策,周三又被其他需求打断,被迫退回。三天像三次轮回,每一次都败给”再等等”。

直到我给开源项目配置了 Dependabot,才发现这个流程早就有一套成熟的解决方案——我把它当问题,别人早已把它当基础设施。

配置 Dependabot

上手比想象中轻。在仓库根目录建一个 .github/dependabot.yml 就行。

它有两层必填:顶部 version: 2 声明语法版本,下面 updates 里,每个包管理器一段。每段三件事:管哪个生态、清单在哪个目录、多久查一次。

默认的全量更新,PR 会有点多。别怕,三个旋钮就能把它调顺。

旋钮一 · allow 白名单 — 只盯你想要的那部分,比如只升生产依赖、只升 patch 和 minor。

旋钮二 · cooldown 冷静期 — 新版本发布头几天先别动,给上游留出踩坑时间。Dependabot 默认就有 3 天冷静期,你还能给大版本单独调长。

旋钮三 · groups 分组 — 把一堆零散小更新并成一个 PR,每周一次”批量合并”,不用逐个点头。

下面是一个完整的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: 2
updates:
  # ───── Maven ─────
  - package-ecosystem: "maven"  # Java 依赖
    directory: "/"
    schedule:
      interval: "weekly" # 每周检查一次
    open-pull-requests-limit: 10 # 同时最多 10 个开放 PR
    labels:
      - "dependencies"
    commit-message:
      prefix: "build" # 提交信息前缀:build(deps): bump

    # 只允许更新生产依赖的 minor/patch 版本
    allow:
      - dependency-type: "production"
        update-types:
          - "version-update:semver-minor"
          - "version-update:semver-patch"

    # 按 semver 级别设置冷却期,防止供应链投毒
    cooldown:
      default-days: 3
      semver-major-days: 14
      semver-minor-days: 5
      semver-patch-days: 1
      exclude:
        - "org.springframework.boot:*"
        - "com.h2database:h2"

    # 分组更新,减少 PR 数量
    groups:
      spring-boot:
        patterns:
          - "org.springframework.boot:*"
          - "org.springframework:*"
        exclude-patterns:
          - "org.springframework.boot:spring-boot-starter-test"
      test-deps:
        dependency-type: "development"
        patterns:
          - "*"

  # ───── GitHub Actions ─────
  - package-ecosystem: "github-actions" # CI 工作流依赖
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    labels:
      - "dependencies"
    commit-message:
      prefix: "build"

    cooldown:
      default-days: 3

    groups:
      all-actions:
        patterns:
          - "*"

GitHub Actions 工作流

Dependabot 管”该不该升级”,GitHub Actions 管”升完能不能用”。这是项目的 CI(持续集成)流水线,每次 push 或 PR 到 main 时自动触发,负责构建、测试和发布质量检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
name: build

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

jobs:
  build:
    name: Build & test (JDK $)
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        # The full reactor includes the jakarta modules (Boot 4 / Servlet 6), so it needs JDK 17+.
        java: [ '17', '21' ]
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK $
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: $
          cache: maven
      - name: Build & test
        run: mvn -B verify

  release-gates:
    name: Release gates (binary compat + SBOM)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
          cache: maven
      - name: Binary-compatibility gate + SBOM
        run: |
          BASELINE=$(mvn -B -q -DforceStdout help:evaluate -Dexpression=compat.baseline.version)
          PROFILES=sbom
          if [ -n "$BASELINE" ] \
             && mvn -B -q dependency:get -Dartifact="cn.chenxinjie:upload-file-core:$BASELINE"; then
            PROFILES=compat-check,sbom
          else
            echo "::warning title=compat gate skipped::baseline ${BASELINE:-<unknown>} is not resolvable; publish it to enable the binary-compatibility gate"
          fi
          mvn -B verify -P"$PROFILES" \
            -pl upload-file-core,upload-file-servlet,upload-file-servlet-jakarta,upload-file-spring-boot-starter,upload-file-spring-boot-starter-jakarta,upload-file-store-jdbc,upload-file-store-redis

当升级从手工 todo 变成自动 PR,当验证从”我跑一遍”变成”流水线跑一遍”,节奏一下子慢了下来。

写在最后:FDE 的三点感想

回头看这两个月,FDE 给我的不是某种新头衔,而是三种朴素的感觉。

1、文档阅读理解非常重要。AI 越强,读文档越要细——它写的代码,得靠你读过的文档来校准。

2、节奏感非常重要。不是越快越好,而是该快时快、该慢时慢,rc.1 到 rc.9 教会我这一点。

3、开发门槛越来越低。门槛低不代表价值低,反而意味着”能不能看懂、能不能把握”,比”能不能写”更值钱。

慢下来,有时候不是退,是为了走得更远。