没有 QA 团队、没有一堆 staging —— 就一个开发者 ship 一个 100 城边缘应用,且不能搞坏它。底气来自 3 段环境链路、真 D1 集成测试(不 mock)、Lighthouse CI 当硬部署卡口。这篇是 DevOps 的故事。
这是系列第 7 篇。
目录
3 段环境链路
每个改动走同样的三段,而且绿了才进下一段:
flowchart LR
L[本地
pnpm dev · dev D1] -->|绿| D[dev worker
真 Workers runtime · dev D1]
D -->|golden path 绿| P[prod
真用户 · prod D1]
P -.->|事故| R[wrangler rollback]| 阶段 | 它证明什么 |
|---|---|
本地(pnpm dev) |
90% 迭代:UI 和逻辑 |
| dev worker | 真 Workers runtime —— OpenNext、边缘 binding、D1 |
| prod | 真用户、真数据 |
规则:别跳过 dev worker 直推 prod。本地服务器抓不到边缘运行时或 binding 问题,只有 dev worker 能。 紧急 hotfix 可以跳 dev,但只在明确决定下 —— 绝不作为常规。D1 migration 永远先 dev 再 prod —— 绝不拿未验证的 migration 打 prod。
4 个依赖,6 种测试
一个刻意小的测试栈覆盖很广:
flowchart TB
V[Vitest] --> U[单元测试]
V --> I[D1 集成 via miniflare]
PW[Playwright] --> E[E2E]
PW --> S[i18n smoke]
PW --> A11Y[a11y via axe-core]
LH[Lighthouse CI] --> WV[Web Vitals]4 个依赖 —— Vitest、Playwright、axe-core、Lighthouse CI —— 覆盖 6 种测试: 单元、D1 集成、 e2e、i18n smoke、无障碍、Web Vitals。小表面、广覆盖、低维护。只有当这 4 个确实覆盖不了某需求时 才加新类型 —— 对工具膨胀保持纪律。
真 D1,不 mock
集成测试经 miniflare 打真本地 D1 —— 真 SQL 打真 SQLite 引擎。Mock D1 不收。 一个返回 你预期值的 mock 证明的是你的 mock,不是你的查询。真 D1 抓得到 schema 不匹配、绑错参数、在你脑子里 成立但 SQLite 里不成立的 SQL。代价 —— 起 miniflare —— 换来一个测真实情况的测试套件,值。
一个值得记录的诚实约束:有些页面依赖 D1 的方式,普通 e2e 环境(不带 D1 跑)服务不了,所以那些测试 CI-skip,改由 D1 集成套件和 dev worker 验证。把约束说清楚,胜过一个只因从没真跑过而「假绿」的测试。
Lighthouse CI 当部署卡口
性能不是感觉 —— 是卡口。Lighthouse CI 跑分档预算(如 Total Blocking Time),回归就挡掉部署。 它抓到过真问题:
- 接入 Google Analytics 把首页 TBT 推过预算。修法不是放松预算 —— 是用
lazyOnload策略加载 GA, 让分析不再阻塞主线程。卡口逼出了对的修法,而不是放一个慢页面上线。 - 预算按页面类型分档,并调到能吸收真实 CI runner 噪声、又不掩盖真回归 —— 紧到能抓问题,松到不狼来了。
性能卡口把「网站最近感觉变慢」变成「这个 PR 超了 TBT 预算,数字在这」—— 可执行,不是感觉。
让测试输出不碍事
一次全量 e2e、一个 268 路由构建、一份 wrangler 上传清单 —— 每个都吐一股输出洪流。和 AI coding
agent 迭代时,这股洪流会淹没工作 context。所以这些重命令交给子代理,在它自己的 context 里读完整日志,
只回 {passed, failed, failures:[...]}。主线看到的是干净摘要,不是一万行。(这是
第 5 篇的 harness/context 纪律,
用到 DevOps 上。)
要点
- 3 段链路(本地 → dev worker → prod),绿了才进,能抓到本地服务器抓不到的边缘运行时问题。
- 4 个测试依赖能覆盖 6 种测试 —— 保持栈小而有纪律。
- 经 miniflare 打真 D1;mock 数据库只测了 mock。
- 用 Lighthouse CI 把性能做成硬卡口 —— 它逼出真修法(如 GA
lazyOnload),而不是放慢页面上线。 - 把吵闹的测试/构建输出卸给子代理,让它回摘要,不是洪流。
常见问题
怎么不 mock 测试 Cloudflare D1 应用? 用 miniflare 起真本地 D1,在集成测试里跑真 SQL。这能抓到 mock 会掩盖的 schema 和查询 bug。
Lighthouse CI 部署卡口是什么? 一个 CI 步骤,拿 Lighthouse 对照设定的性能预算(如 Total Blocking Time)跑,回归就让构建失败, 挡掉部署直到性能修好。
单人开发者没 QA 怎么放心 ship? 分段链路(本地 → dev → prod)绿了才进、小而广的自动化测试栈、真 D1 集成测试、性能卡口、快速 rollback。
为什么先部署到 dev worker 再 prod? 本地 dev 服务器复现不了真 Workers runtime、边缘 binding 或 D1 行为。dev worker 在真用户看到前验证这些。