从 JHipster 6 跟到 9.x(Spring Boot 4、Java 21、Angular 21),我们平均每 12-18 个月做一次大版本跨越,小版本则随安全补丁随时跟进。这篇把这个过程方法论化:版本节奏怎么理解、成本从哪来、三条升级路线怎么选、哪些纪律能让下次升级便宜一半,以及什么时候我们决定不升了。
版本节奏与”要不要跟”
JHipster 大体维持半年一个 major 的发布节奏(8.x 到 9.x 间隔约一年半,中间穿插大量 minor/patch)。major 意味着生成模板大改:9.x 这次就带了生成器 TypeScript 重写、yeoman v8、Spring Boot 4 与 Java 21 强制。跟不跟的判断框架:
| 情形 | 建议 |
|---|---|
| 生命周期 > 2 年的项目 | major 最多跳一个版本(6→8 可以,6→9 谨慎),否则积压成本指数增长 |
| 有安全 CVE 压力(Spring/依赖) | 优先升级,或至少做依赖热修 |
| 团队正在大规模改业务代码 | 错峰,业务封版后再升 |
| 项目进入纯维护期 | 跟安全 patch,major 可冻结,但要写进决策记录 |
我们的教训:JHipster 7 → 8 拖了一年半,结果 Spring Boot 3 的 Jakarta 命名空间迁移和 JHipster 8 的改动叠加,diff 大到 review 了一周。升级成本随跨越版本数超线性增长,宁可小步多次。
升级成本从哪来
生成代码不是普通依赖,升级时它的成本结构特殊:
- 生成代码冲突:你手改过的生成文件,重新生成时必然冲突。这是最大头,也是第 23 篇”不改生成代码哲学”存在的原因。
- 依赖 breaking:Spring Boot major(如 3 的 Jakarta、4 的部分 API 收紧)、Angular major(21 的 Zoneless 迁移收尾)、Node 版本要求(9.x 要 Node 22)。
- 配置项迁移:
application.yml里的配置键随 Spring Boot 版本改名/废弃,JHipster 自身的jhipster.*命名空间也会调整。 - 构建工具链:Maven 3.9、前端构建链、Docker 基础镜像的同步更新。
其中 1 完全可控,2/3/4 社区已趟过、有 release notes 与迁移指南可抄。
三条升级路线
路线一:jhipster upgrade 子生成器
npm install -g generator-jhipster@9.x
cd myapp
jhipster upgrade --from 8.7.0 --to 9.0.0
原理:工具把你的项目按新旧两个版本各生成一份(利用 .yo-rc.json 与 .jhipster/*.json),三方合并生成代码的 diff,再把这份 diff 应用到你的分支上,最后留一个待解决的冲突清单。适合:生成代码改得少、跨一个 major 以内的项目。产出是一个巨大的 PR,review 压力集中。
路线二:migrate blueprint
npm install -g generator-jhipster-cli
jhipster migrate
官方的 generator-jhipster-migrate blueprint 扫描项目与最新模板的差异,按文件给出”接受新模板 / 保留本地版本 / 手工合并”的交互式选择,增量式走完整个升级。适合:跨多个 major、或想分模块分批推进的大项目。9.x 重写后的生成器对这条路支持更好,我们 8→9 就是用它分三次 PR 走完的。
路线三:手动 diff 重生成
最朴素也最可控:新目录里按 .yo-rc.json 用新版本生成一份”纯净工程”,然后逐目录与老工程 diff:
mkdir /tmp/myapp-fresh && cd /tmp/myapp-fresh
cp $OLD/.yo-rc.json .
cp -r $OLD/.jhipster .
jhipster --with-entities # 按事实源整体重生成
diff -ru $OLD/src /tmp/myapp-fresh/src | less
适合:升级同时要清理历史包袱、或有定制 blueprint 的团队。成本最高但每一步都在掌控中。
| 维度 | upgrade 子生成器 | migrate blueprint | 手动 diff |
|---|---|---|---|
| 适用跨度 | ≤1 个 major | 跨多 major | 任意 |
| 交互粒度 | 一次性大 PR | 按文件增量 | 完全自定义 |
| 人工投入 | 中 | 中低 | 高 |
| 风险 | 冲突堆积 review 困难 | 依赖 blueprint 更新 | 流程靠人,易漏 |
| 我们的使用频率 | 早期常用 | 当前主力 | 半年一次的大清理 |
事实源:升级便宜的根因
三条路线全部依赖同一组”再生事实源”:
.yo-rc.json # 应用级选项:技术栈、认证方式、包名、语言
.jhipster/
Customer.json # 每个实体的字段、关系、DTO/服务层选项
OrderItem.json
myapp.jdl # (可选)等价 JDL 表达,随生成维护
只要它们与代码同步,“用新版本重新生成整个应用”永远是可行操作,升级就只是 diff 管理问题。反之,如果实体是在老版本里生成后 .jhipster/*.json 被删了或忘了随字段修改更新,生成器就再也还原不出当前代码,升级立刻退化为纯手工迁移。我们的纪律:
.yo-rc.json、.jhipster/全部进 git,实体结构变更必须同步改 JDL/JSON 后重新生成(jhipster entity Customer --single-entity),而不是直接改生成代码;- review 时把”改了生成文件却没改事实源”视为缺陷。
锁定生成代码的修改策略
升级冲突量 ≈ 你改过的生成文件数。策略按侵入度排序:
- 不动:业务逻辑放新包/新模块(第 23 篇),生成文件保持出厂状态。
- 标记性扩展:必须改的生成文件(如
SecurityConfiguration加一条规则),改动集中、注释标记// CUSTOM:,升级时按标记搬运。 - fork 模板:改动面大且通用 → 做成 blueprint(第 24 篇),让生成器原生产出你要的样子。
我们统计过 8→9 升级:手改生成文件 23 个的模块 review 加迁移花了 6 人日;同期一个几乎零手改的模块只花 1 人日。差距就是哲学的差距。
升级前的 git 准备与回归测试
标准流程,缺一不可:
# 1. 干净起点:main 合并完所有在途分支
git checkout main && git pull
# 2. 独立升级分支
git checkout -b upgrade/jhipster-9
# 3. 打 tag 便于回退对照
git tag pre-jhipster-9
# 4. 跑升级工具(任选路线)
# 5. 逐文件解决冲突后,全量回归
./mvnw verify # 后端单测 + Testcontainers 集成测试(第 17 篇)
npm run e2e # Cypress 端到端
回归底线:测试套件是升级的保险绳,覆盖不足的项目先补关键路径的集成测试再谈升级(第 17 篇的分层测试在这时兑现价值)。数据库侧还要做 Liquibase 前滚演练(第 7 篇)。
小版本与补丁怎么跟
major 之外,minor/patch 的跟进策略我们固化为两条流水线:
| 类型 | 触发 | 时限 | 动作 |
|---|---|---|---|
| 安全 patch(CVE High+) | 依赖扫描告警 | 3 个工作日 | 只升受影响依赖,mvn dependency-management 锁定,走热修分支 |
| JHipster minor | release notes | 下个迭代 | 随常规迭代合并,重生成 diff 通常很小 |
配套一个 CI 对账任务:每天比对本地 package.json/pom.xml 与 JHipster 当前 minor 的依赖清单,输出漂移报告。避免”生成器停在 9.0、手工依赖悄悄升到 9.2 等价版本”的隐性漂移——下次升级时这些漂移全部变成额外 diff。此外团队约定所有人用同一生成器版本(npm ls -g generator-jhipster 对齐),本地 9.0 与 9.1 生成的实体文件细微差异会在 review 里制造噪音。
何时放弃升级
三种情况我们认真评估过”不升”:
- 项目剩一年维护期:冻结版本,只做依赖安全热修,把升级预算留给接替系统;
- 生成代码已被改得面目全非:事实源失效,重生成等于重写——先做”生成代码回归出厂”的重构(成本另计),否则升级无解;
- 技术栈分叉:例如前端早已迁出 Angular 换成别的方案,只升后端,此时把 JHipster 当普通 Spring Boot 工程对待,只跟 Spring Boot 版本。
放弃不是失败,是把升级成本和收益摆上台面后的理性决策——前提是决策写进 ADR,半年后复审一次而不是永远搁置。
小结
- 半年一个 major 的节奏下,升级要”小步、跟紧、错峰”;跨越版本越多成本越超线性。
- 三条路线按项目健康度选:upgrade 子生成器适合小跨度、migrate blueprint 是当前主力、手动 diff 兜底大清理。
.yo-rc.json与.jhipster/*.json是再生事实源,与代码同步是升级便宜的根本前提。- 生成代码少改、标记改、或固化进 blueprint,是给未来升级送的最好礼物。
系列导航
- 上一篇:多环境配置管理
- 下一篇:自定义业务模块——不改生成代码的哲学
- 相关阅读:配置项随版本迁移的细节见第 21 篇;生成代码隔离度直接决定升级成本(第 23 篇);升级踩过的坑集中收录在踩坑全集