435 lines
21 KiB
Markdown
435 lines
21 KiB
Markdown
# 互换模块长期改进方案——从"一个需求改一周"到"可预测交付"
|
||
|
||
> 基于 EQD-6838(债券三字段互算)从 0aad4c80 到 ffa51c6a 共 106 次提交、
|
||
> 7 天开发周期、50 次 fix/revert(47%)的惨痛教训,提炼出的长远改进路线。
|
||
> 不是一次性重构方案,而是"每个迭代都往前走一步"的渐进式改善。
|
||
|
||
---
|
||
|
||
## 〇、项目架构现状(接手必读)
|
||
|
||
### 0.1 整体技术栈
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 宿主应用 (zszq-trs / YLErpWeb) │
|
||
│ ├── 后端: ASP.NET Core 6 MVC + Razor Views (600 个 cshtml) │
|
||
│ ├── 前端: jQuery 3.5 + Vue 2 + Bootstrap + JqGrid │
|
||
│ ├── 规模: 2450 个 C# 文件, 289 个 JS 文件, 67 个 new Vue() │
|
||
│ └── 微前端: qiankun v2.10.5 (加载 otcdms-ui v3 子应用) │
|
||
│ └── Vue3 独立项目,部署在 /otcdms-ui/ 路径 │
|
||
│ 宿主通过 /v3 路由激活微应用 │
|
||
│ 通信: qiankun.initGlobalState (logout/查看交易等) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 0.2 两套 Layout + 两套 Bundle
|
||
|
||
项目有**两套独立的页面布局**,各自加载不同的 JS/CSS bundle。一个页面只会加载其中一套,互不冲突。
|
||
|
||
#### bundle.js — 交易管理主站
|
||
|
||
| 属性 | 值 |
|
||
|------|-----|
|
||
| 加载者 | `_MainLayout.cshtml` / `_InfoLayout.cshtml` / `_LayoutMini.cshtml` |
|
||
| 用在哪 | 互换交易、期权交易、风控、客户管理等业务页面 |
|
||
| 文件大小 | 823 KB |
|
||
| 包含 main.js? | ✅ (第 49 行) |
|
||
| 包含 fastVue? | ✅ (fastVue.base.js + fastVue.components.js) |
|
||
| UI 框架 | Bootstrap 3 + JqGrid 4 + jquery-confirm |
|
||
| 日期库 | dayjs + 插件 |
|
||
|
||
`_MainLayout.cshtml` 的加载顺序:
|
||
```html
|
||
<head>
|
||
<link href="bundle.min.css"> ← 样式
|
||
<script src="jquery.js"> ← jQuery 3.5 + jQuery UI + layer + numeral
|
||
</head>
|
||
<body>
|
||
@RenderBody() ← 页面内容
|
||
<script src="bundle.js"> ← lodash + bootstrap + JqGrid + fastVue + main.js
|
||
<script src="vue.js"> ← Vue 2.6 + vue.custom
|
||
<script src="mainLayout.js"> ← 布局交互
|
||
<script src="qiankun-v2.10.5/index.umd.min.js"> ← 微前端运行时
|
||
</body>
|
||
```
|
||
|
||
#### bundleV2.js — 系统管理后台
|
||
|
||
| 属性 | 值 |
|
||
|------|-----|
|
||
| 加载者 | `Areas/Admin/Views/Shared/_Layout.cshtml` |
|
||
| 用在哪 | 系统管理后台(AppConfig、OtcFormat、数据库升级、确认书信息等) |
|
||
| 文件大小 | 562 KB |
|
||
| 包含 main.js? | ✅ (第 123 行) |
|
||
| 包含 fastVue? | ❌ |
|
||
| UI 框架 | Bootstrap 4 (tabler) + bootstrap-table + toastr |
|
||
| 与 bundle.js 的区别 | 自带 jQuery + Vue(不依赖 jquery.js/vue.js),用 bootstrap-table 替代 JqGrid |
|
||
|
||
**为什么改 main.js 后两个 bundle 都要重建**:因为 `main.js` 同时出现在两个 bundle 的 `inputFiles` 中(`bundleconfig.json` 第 49 行和第 123 行)。`rebuild-bundles.py` 会自动重建全部 6 个产物,不需要手动选择。
|
||
|
||
#### 完整 Bundle 清单
|
||
|
||
| 产物 | 大小 | 内容 | 被谁加载 |
|
||
|------|------|------|---------|
|
||
| `jquery.js` | 340 KB | jQuery 3.5 + jQuery UI + layer + numeral + validate | 主站 (`_MainLayout` 等) |
|
||
| `bundle.js` | 823 KB | lodash + dayjs + Bootstrap 3 + JqGrid + fastVue + main.js + jqGridEx | 主站 |
|
||
| `vue.js` | 97 KB | Vue 2.6 min + vue.custom | 主站 |
|
||
| `bundleV2.js` | 562 KB | lodash + jQuery + Bootstrap 4 + bootstrap-table + main.js + Vue 2 | 管理后台 |
|
||
| `bundle.css` | 358 KB | Bootstrap 3 CSS + JqGrid CSS + 公共样式 | 主站 |
|
||
| `bundleV2.css` | 409 KB | tabler CSS + toastr CSS + bootstrap-table CSS | 管理后台 |
|
||
|
||
### 0.3 qiankun 微前端架构
|
||
|
||
项目已通过 qiankun 实现渐进式现代化,部分新页面在 `otcdms-ui v3`(Vue3 独立项目)中开发:
|
||
|
||
```
|
||
宿主 (zszq-trs) 微应用 (otcdms-ui v3)
|
||
┌──────────────────────┐ ┌──────────────────────┐
|
||
│ _MainLayout.cshtml │ │ Vue3 独立项目 │
|
||
│ └─ qiankun-v2.10.5 │──register──→ │ entry: /otcdms-ui/ │
|
||
│ registerMicroApps │ │ activeRule: /v3 │
|
||
│ start() │ │ │
|
||
└──────────┬───────────┘ └──────────────────────┘
|
||
│ initGlobalState
|
||
│ (logout, retrieveTradeDetail,
|
||
│ quotaTrial, retrieveSwapDetail...)
|
||
↕
|
||
V3/Index.cshtml: /v3 开头的路由
|
||
自动加载微应用到 #subapp-viewport
|
||
```
|
||
|
||
**对改进方案的影响**:
|
||
- 新页面走 v3 微应用(Vue3),不再受 jQuery/Vue2 踩坑困扰
|
||
- 存量页面(如 `swapTradeEdit.js`)仍在宿主中,是踩坑主战场
|
||
- 阶段 2 组件可测试化的优先级取决于**该页面是否会迁移到 v3**:
|
||
- 短期不迁移的页面 → 值得做组件可测试化
|
||
- 已计划迁移的页面 → 不值得投入,直接在 v3 中重写
|
||
|
||
---
|
||
|
||
## 一、数据事实:到底有多痛
|
||
|
||
| 指标 | 数值 | 含义 |
|
||
|------|------|------|
|
||
| 开发周期 | 7 天 | 一个"小需求"从开始到可用 |
|
||
| 提交总数 | 106 次 | 日均 15 次提交 |
|
||
| 修复/回退/调试 | 50 次(47%) | 近一半提交是在修自己引入的 bug |
|
||
| Revert | 10 次 | 10 次回退(含精度调整批量回退 6 次) |
|
||
| Merge | 21 次 | 7 天内 21 次合并,多人多分支并行冲突频繁 |
|
||
| Bundle/构建 | 8 次 | 8 次提交只为解决"改了源文件没生效" |
|
||
| console.log 调试 | 是 | 不得不靠打印日志+部署来试错 |
|
||
|
||
**对比行业基准**:成熟团队的 fix 提交占比通常在 10-20%。47% 意味着每写 2 行代码就要花 1 行来修 bug。
|
||
|
||
---
|
||
|
||
## 二、根因分析:为什么会这样
|
||
|
||
### 2.1 架构层面:jQuery 与 Vue 的"伪混用"
|
||
|
||
这是**所有踩坑的总根源**。项目有 67 个 `new Vue(...)` 组件,但它们不是真正的 Vue 组件——它们是 jQuery 代码包了个 Vue 壳:
|
||
|
||
```
|
||
当前架构(问题根源):
|
||
cshtml (Razor)
|
||
↓ 引入 bundle.js (801KB,包含 lodash/jquery/vue/fastVue)
|
||
↓ 引入 swapTradeEdit.js (独立 script,1963 行)
|
||
↓ new Vue({ methods: { ... jQuery 逻辑 ... } })
|
||
↓ vue-number-input 组件 = jQuery.numberInput 包了个 Vue template
|
||
|
||
真正的问题:
|
||
- jQuery 事件系统覆盖 Vue 事件代理 → .native 失效
|
||
- jQuery $() 操作 DOM 绕过 Vue 响应式 → 视图不更新
|
||
- jQuery Deferred 与 Vue 生命周期不同步 → 回调时机不可控
|
||
- 组件没有 props 验证 / 没有模板编译 → 运行时才发现错误
|
||
```
|
||
|
||
**这不是"Vue2 不行"**,而是 Vue 的响应式契约被 jQuery 破坏了。纯 Vue2 原生组件不会出现这些问题。
|
||
|
||
### 2.2 构建层面:Bundle 是手工艺品
|
||
|
||
项目有 6 个 bundle 产物(见 0.2 节),其中 `main.js` 同时出现在 `bundle.js` 和 `bundleV2.js` 中——改了 `main.js` 必须同时重建两个 bundle,否则一个页面生效另一个不生效。
|
||
|
||
```
|
||
当前流程:
|
||
改源文件 → 手动跑 rebuild-bundles.py → 提交 bundle.js + bundleV2.js → dotnet publish → 部署
|
||
|
||
问题链:
|
||
① 忘记重新打 bundle → 部署的是旧代码
|
||
② 只重建了 bundle.js 忘了 bundleV2.js → 管理后台行为不一致
|
||
③ BOM/换行符不一致 → bundle 产物有差异
|
||
④ Linux 路径大小写敏感 → CI 构建失败
|
||
⑤ JsVersion 缓存键不刷新 → 浏览器加载旧缓存
|
||
⑥ bundle.js 823KB + bundleV2.js 562KB 被提交到 git → 每次改动产生巨大 diff
|
||
```
|
||
|
||
**已改善**:`rebuild-bundles.py` 已自动重建全部 6 个产物;MSBuild `GenerateBundlesBeforeBuild` target 在 Build 前自动调用;CI 脚本 `run-ci-checks.sh` 的 `--verify` 模式可检测产物与源文件不同步。但产物仍提交到 git,diff 噪音问题未解决。
|
||
|
||
### 2.3 测试层面:纯函数绿 ≠ 系统对
|
||
|
||
```
|
||
已有测试:
|
||
C# 单测:133 个文件,37933 行(主要测后端 BLL)
|
||
JS 单测:22 个文件(主要测 swapCalc.js 纯函数)
|
||
|
||
缺失的测试层:
|
||
✗ 前端组件测试(@vue/test-utils)
|
||
✗ 事件链路测试(keydown → emit → handler → state)
|
||
✗ API 集成测试(main.post resolve/reject 链路)
|
||
✗ 前后端契约测试(/Bond/CalcBond 的请求/响应格式)
|
||
✗ E2E 测试(从用户输入到数据库落库的全链路)
|
||
```
|
||
|
||
纯函数测试全绿,但集成层每层都有断裂点。这导致"改了纯函数以为修好了 → 部署发现没生效 → 再改集成层 → 再部署"的循环。
|
||
|
||
### 2.4 流程层面:没有"安全网"的开发
|
||
|
||
| 问题 | 当前状态 | 后果 |
|
||
|------|---------|------|
|
||
| 本地验证 | 只能 `dotnet publish` + 部署测试环境 | 每轮验证 10+ 分钟 |
|
||
| 代码审查 | 无强制 review | 质量取决于个人经验 |
|
||
| CI/CD | 有 Jenkins 但不跑前端测试 | 前端 bug 到部署后才发现 |
|
||
| 分支策略 | 多人直接 merge 到 feature 分支 | 21 次 merge = 21 次冲突风险 |
|
||
| 回归测试 | 靠人工 | 改 A 坏 B(如精度调整批量回退 6 次) |
|
||
|
||
### 2.5 领域知识层面:字段命名混乱
|
||
|
||
已在《互换价格字段命名规范决策文档》中详细记录:`PosiNetPrice` 名为"Net"实为"含费全价",连文档作者都被误导。这种命名混乱导致每次改动都要反复核对数据流向,极易引入新 bug。
|
||
|
||
---
|
||
|
||
## 三、改进路线:5 个阶段,每阶段都有可交付产出
|
||
|
||
### 阶段 0:止血(1-2 周,零风险改动)— ✅ 已实施
|
||
|
||
**目标**:不改架构,只加"安全网",让下一个需求不再靠 console.log 试错。
|
||
|
||
| 动作 | 产出 | 风险 | 状态 |
|
||
|------|------|------|:---:|
|
||
| `swapCalc.js` 纯函数测试 | 所有计算逻辑有回归守卫 | 零 | ✅ 已完成 |
|
||
| `vue-number-input` 事件链路测试 | keydown/enter/paste 可自动验证 | 零 | ✅ 已完成 |
|
||
| `main.post` resolve/reject 链路集成测试 | Promise 失败分支不遗漏 | 零 | ✅ 已完成 |
|
||
| `main.postSafe()` Promise 封装 | 新代码可用 async/await 替代 Deferred | 零 | ✅ 已完成 |
|
||
| pre-commit hook(guard_arch + jest) | 提交前自动验证架构+测试 | 零 | ✅ 已完成 |
|
||
| CI 脚本 `run-ci-checks.sh` | guard_arch + jest + bundle 校验 | 零 | ✅ 已完成 |
|
||
| `jest.config.js` + 覆盖率配置 | 测试配置标准化 | 零 | ✅ 已完成 |
|
||
| `otcformat.js` tradeSinglePrice 精度修复 | precision 2→9,修复 pre-existing bug | 低 | ✅ 已完成 |
|
||
|
||
**当前测试状态**:10 suites, 190 tests, 全绿(commit `6b32f254`)。
|
||
|
||
**安装方式**(每位开发者执行一次):
|
||
```bash
|
||
cp YLErpWeb/fe-tests/hooks/pre-commit .git/hooks/pre-commit
|
||
chmod +x .git/hooks/pre-commit
|
||
# 需要 Node.js 18+ (nvm install 20)
|
||
# 首次: cd YLErpWeb/fe-tests && npm install
|
||
```
|
||
|
||
**为什么先做这个**:纯函数测试已经在本次迭代中证明了价值——`bondCalc.test.js` 全绿说明逻辑正确,问题只在集成层。补上集成层测试后,下一个需求的"改了不生效"可以在本地 1 秒内发现(jest 运行时间),而不是部署后 10 分钟。
|
||
|
||
### 阶段 1:构建现代化(2-3 周)
|
||
|
||
**目标**:消灭 bundle 手工艺品,让"改了源文件不生效"成为历史。
|
||
|
||
**当前已改善的部分**:`rebuild-bundles.py` + MSBuild target + CI `--verify` 已解决"忘了打 bundle"和"产物与源不同步"的问题。但产物仍提交到 git,diff 噪音和缓存问题未解决。
|
||
|
||
| 动作 | 产出 | 风险 |
|
||
|------|------|------|
|
||
| 引入 esbuild 做 JS 打包 | 200ms 增量构建 | 低(产物逐字节比对验证) |
|
||
| bundle.js + bundleV2.js 从 git 移除,改为 CI 构建 | git diff 不再有 823KB+562KB 噪音 | 低 |
|
||
| `JsVersion` 缓存键改为 content hash | 浏览器永远加载最新版本 | 低 |
|
||
| `.editorconfig` 统一换行符/BOM 规则 | 不再有 BOM/换行符问题 | 零 |
|
||
| Source map 上线 | 生产环境可定位到源文件行号 | 零 |
|
||
|
||
**关于 esbuild 的评估**:本项目 bundle 是纯文件拼接(非 ES module 打包),esbuild 的核心价值(tree-shaking / code splitting)在当前阶段用不上。esbuild 的真正价值在于阶段 2——当组件方法被提取为 `import/export` 模块后,esbuild 才能发挥优势。当前阶段保留 `rebuild-bundles.py` 即可,等阶段 2 再引入 esbuild。
|
||
|
||
**预期收益**:本次迭代中 8 次 bundle 相关提交全部可以避免。
|
||
|
||
### 阶段 2:前端组件可测试化(3-4 周/模块)
|
||
|
||
**目标**:让每个 Vue 组件都能脱离浏览器独立测试。
|
||
|
||
**策略**:不一次性重写,而是**逐模块迁移**。每次只改一个页面,不影响其他页面。
|
||
|
||
**与 qiankun 迁移的关系**:如果一个页面已计划迁移到 otcdms-ui v3(Vue3),则不值得在旧代码上做组件可测试化,直接在 v3 中重写即可。优先改那些**短期不会迁移**的页面。
|
||
|
||
```
|
||
迁移优先级(按 bug 密度 + 迁移可能性排序):
|
||
1. swapTradeEdit.js (1963 行,本次踩坑主战场,短期不迁移)
|
||
2. unwindSwapTrade.js (536 行,平仓逻辑复杂,短期不迁移)
|
||
3. incomeSwapTrade.js (390 行,结息逻辑,短期不迁移)
|
||
4. 已计划迁移到 v3 的页面 → 跳过,直接在 v3 中重写
|
||
5. 其他模块
|
||
```
|
||
|
||
每个模块的迁移步骤:
|
||
1. 把 `new Vue({ methods: { ... } })` 中的方法提取为独立模块(如 `swapTradeEditHandler.js`)
|
||
2. 方法只接收 `state` 和 `event` 参数,不依赖 `this`
|
||
3. 用 jest 测试 handler 模块
|
||
4. Vue 组件只做 `v-on:keydown="handler.onBondPriceKeydown(item, type, $event)"` 的薄绑定
|
||
5. 用 `@vue/test-utils` mount 组件验证事件绑定
|
||
|
||
**关键原则**:
|
||
- **不改业务逻辑**,只改代码组织方式
|
||
- **不改 cshtml 模板**(模板已经是 Vue 语法)
|
||
- **不改后端 API**
|
||
- 每步都有测试覆盖后才继续下一步
|
||
|
||
### 阶段 3:jQuery 逐步退场(持续进行)
|
||
|
||
**目标**:消除 jQuery 与 Vue 的事件系统/响应式冲突。
|
||
|
||
**策略**:不一次性替换 jQuery,而是**逐个组件替换 jQuery 依赖**。新页面走 v3 微应用(Vue3),天然不含 jQuery;存量页面借修 bug 的机会逐步替换。
|
||
|
||
| jQuery 用途 | 替代方案 | 迁移难度 | 状态 |
|
||
|-------------|---------|---------|:---:|
|
||
| `$.ajax` / `main.post` | `main.postSafe()` (Promise 封装) | 低 | ✅ 已提供 |
|
||
| `$(el).on('keydown')` | `el.addEventListener` + Vue `$emit` | 低 | ✅ 已在 vue-number-input 中验证 |
|
||
| `$.Deferred` | `Promise` / `async-await` | 低 | 通过 `main.postSafe` 间接解决 |
|
||
| `$.confirm` / `$.alert` | 自定义 Vue 弹窗组件 | 中 | 待做 |
|
||
| `$(selector)` DOM 操作 | Vue `ref` + `data` 绑定 | 高 | 待做 |
|
||
| jQuery UI autocomplete | Vue autocomplete 组件 | 中 | 待做 |
|
||
|
||
**已完成的过渡方案**:`main.postSafe()` 已在 `main.js` 中实现(commit `6b32f254`),返回标准 Promise 而非 jQuery Deferred,新代码可以直接用 `async/await`,不再需要记住"业务错误走 reject、.done 不触发"的陷阱。旧代码不受影响。
|
||
|
||
**迁移节奏**:每次改一个页面时,顺手把该页面的 jQuery 依赖替换掉。不为了迁移而迁移,而是"借修 bug 的机会还技术债"。
|
||
|
||
### 阶段 4:字段命名规范化(持续进行)
|
||
|
||
**目标**:消除"PosiNetPrice 名为 Net 实为含费全价"这类命名混乱。
|
||
|
||
按《互换价格字段命名规范决策文档》的落地策略,分阶段执行:
|
||
- 阶段 4a:给最混乱的字段加 `[Obsolete]` + XML 注释(零风险)
|
||
- 阶段 4b:新增字段/新功能强制用规范命名
|
||
- 阶段 4c:前端统一取值封装(`swapPriceHelper.js`)
|
||
- 阶段 4d:大版本升级时做 DB 列重命名
|
||
|
||
---
|
||
|
||
## 四、每个阶段的具体执行清单
|
||
|
||
### 阶段 0 止血——执行清单 — ✅ 已完成
|
||
|
||
```
|
||
✅ 1. pre-commit hook (guard_arch + jest)
|
||
- cp YLErpWeb/fe-tests/hooks/pre-commit .git/hooks/pre-commit
|
||
- 提交前自动跑架构闸门 + 前端单测
|
||
|
||
✅ 2. 集成测试 (bondCalc.integration.test.js)
|
||
- 覆盖 Vue 响应式 / jQuery 事件 / main.post reject 链路
|
||
|
||
✅ 3. main.postSafe() Promise 封装
|
||
- 新代码可用 async/await 替代 jQuery Deferred
|
||
- 旧代码 (main.post) 不受影响
|
||
|
||
✅ 4. jest.config.js + 覆盖率配置
|
||
- testEnvironment: node
|
||
- collectCoverageFrom + coverageThreshold
|
||
|
||
✅ 5. CI 脚本 (run-ci-checks.sh)
|
||
- 3 项检查: guard_arch + jest + bundle 校验
|
||
- Jenkins/GitLab CI 通用
|
||
|
||
✅ 6. otcformat.js tradeSinglePrice precision 修复 (2→9)
|
||
- 修复 80fbb184 (EQD-6597) 引入的 pre-existing bug
|
||
- otcformat.test.js 守卫测试现在全绿
|
||
|
||
⬜ 7. Jenkins pipeline 接入 run-ci-checks.sh (待 CI 管理员配置)
|
||
```
|
||
|
||
### 阶段 1 构建现代化——执行清单
|
||
|
||
```
|
||
□ 1. 引入 esbuild
|
||
- npm install esbuild
|
||
- 写 build.js 脚本:把 bundleconfig.json 的输入文件列表用 esbuild 打包
|
||
- 产出与现有 bundle.js 逐字节比对(允许差异但需验证功能一致)
|
||
|
||
□ 2. CI 自动构建 bundle
|
||
- dotnet publish 前自动跑 esbuild
|
||
- 不再提交 bundle.js 到 git
|
||
|
||
□ 3. JsVersion 改为 content hash
|
||
- _MainLayout.cshtml 中 bundle.js 的版本号改为文件内容的 hash
|
||
- 浏览器永远加载最新版本
|
||
|
||
□ 4. 统一 .editorconfig
|
||
- end_of_line = lf
|
||
- charset = utf-8(不带 BOM)
|
||
- insert_final_newline = true
|
||
```
|
||
|
||
### 阶段 2 组件可测试化——执行清单(以 swapTradeEdit 为例)
|
||
|
||
```
|
||
□ 1. 提取 handler 模块
|
||
- 创建 swapTradeEditHandler.js
|
||
- 把 onBondPriceEnter/onBondPriceEdit/onBondPriceKeydown/
|
||
onDpPriceInput/calcBondForItem/syncBondFlags 等方法搬出
|
||
- 方法签名改为 function(state, event, ...args),不依赖 this
|
||
|
||
□ 2. 为 handler 写测试
|
||
- 每个方法至少 3 个用例:正常路径、边界值、错误路径
|
||
- mock main.post 的 resolve/reject
|
||
|
||
□ 3. Vue 组件改为薄绑定
|
||
- methods 中只做 handler(state, event) 的调用
|
||
- 不含任何业务逻辑
|
||
|
||
□ 4. 用 @vue/test-utils 验证组件
|
||
- mount 组件
|
||
- 模拟 keydown/enter/input 事件
|
||
- 断言 state 变化 + 视图渲染
|
||
```
|
||
|
||
---
|
||
|
||
## 五、优先级矩阵
|
||
|
||
```
|
||
高收益
|
||
│
|
||
阶段0止血 │ 阶段1构建
|
||
←───────────────┼───────────────→
|
||
低风险 │ 中风险
|
||
│
|
||
阶段2测试 │ 阶段3去jQuery
|
||
│ 阶段4命名
|
||
│
|
||
低收益
|
||
```
|
||
|
||
**执行顺序**:阶段0 → 阶段1 → 阶段2(逐模块)→ 阶段3(逐组件)→ 阶段4(持续)
|
||
|
||
**关键原则**:每个阶段都是**可交付的独立产出**,即使只完成阶段0,下一个需求的踩坑频率也会大幅下降。
|
||
|
||
---
|
||
|
||
## 六、预期收益量化
|
||
|
||
| 改进措施 | 预期减少的提交 | 预期减少的调试时间 |
|
||
|---------|:---:|:---:|
|
||
| 阶段0:集成测试 + CI | -15 次(集成层 bug 本地发现) | -2 天 |
|
||
| 阶段1:构建现代化 | -8 次(bundle 相关全消除) | -0.5 天 |
|
||
| 阶段2:组件可测试化 | -10 次(响应式/事件 bug 本地发现) | -1.5 天 |
|
||
| 阶段3:去 jQuery | -5 次(Promise/事件类 bug 消除) | -0.5 天 |
|
||
| 阶段4:命名规范 | -5 次(字段混淆类 bug 消除) | -0.5 天 |
|
||
| **合计** | **-43 次(40%)** | **-5 天** |
|
||
|
||
从 7 天降到 2 天,从 106 次提交降到 60 次左右,fix 占比从 47% 降到 20% 以下。
|
||
|
||
---
|
||
|
||
## 七、不走弯路的注意事项
|
||
|
||
1. **不要一次性重写**。这个项目有 2450 个 C# 文件、289 个 JS 文件。一次性重写 = 一次性引入 100 个新 bug。渐进式改造是唯一可行路径。
|
||
|
||
2. **不要为了迁移而迁移**。每次迁移都应该是"借修 bug 的机会还债",而不是"停下所有业务开发来做技术改造"。业务方不会接受后者。
|
||
|
||
3. **不要跳过测试直接改架构**。如果当前代码没有测试覆盖,改架构等于裸奔——你不知道改动是否破坏了什么。先加测试(阶段0),再改架构(阶段2-3)。
|
||
|
||
4. **不要忽视构建工具**。bundle 问题看起来是"小事",但它消耗了 8 次提交和大量调试时间。构建工具是基础设施,基础设施不稳,上面的一切都不稳。
|
||
|
||
5. **不要指望工具解决流程问题**。CI、测试、构建工具都是"安全网",但它们不能替代代码审查和设计文档。每个新需求仍需要:先写设计文档 → 识别风险点 → 补测试 → 再写代码。
|