发布与上线
概览
带着把握去发布。目标不只是“部署成功”,而是要在有监控、有回滚预案、清楚知道成功标准的前提下安全发布。每一次上线都应该具备可回退、可观测和渐进推进三个特征。
何时使用
- 某个功能第一次发到生产环境
- 向用户发布重大改动
- 做数据或基础设施迁移
- 打开 beta 或 early access 计划
- 任何带风险的部署,实际上也就是所有部署
发布前检查清单
代码质量
- 所有测试通过,包括 unit、integration、e2e
- 构建成功且无警告
- Lint 与类型检查通过
- 代码已评审并批准
- 没有必须在发布前解决的 TODO 注释
- 生产代码里没有
console.log调试语句 - 错误处理覆盖了预期失败模式
安全
- 代码与版本控制中没有 secrets
-
npm audit没有 critical 或 high 漏洞 - 所有对用户开放的 endpoint 都做了输入校验
- 已配置认证与授权检查
- 安全头已配置,例如 CSP、HSTS
- 认证相关接口具备限流
- CORS 只允许明确来源,而不是通配符
性能
- Core Web Vitals 处于 “Good” 区间
- 关键路径上没有 N+1 查询
- 图片已优化,包括压缩、自适应尺寸、懒加载
- Bundle 大小在预算内
- 数据库查询有合适索引
- 静态资源与高频查询已配置缓存
可访问性
- 所有交互元素都支持键盘导航
- 屏幕阅读器能正确传达页面内容与结构
- 颜色对比满足 WCAG 2.1 AA,例如正文 4.5:1
- Modal 和动态内容的焦点管理正确
- 错误信息足够清晰,并与表单字段关联
- axe-core 或 Lighthouse 中没有无障碍警告
基础设施
- 生产环境变量已设置
- 数据库迁移已应用,或已准备好应用
- DNS 与 SSL 已配置
- CDN 已为静态资源配置
- 日志和错误上报已配置
- 健康检查接口存在且响应正常
文档
- README 已更新任何新的启动要求
- API 文档是最新的
- 任何架构决策都已写 ADR
- Changelog 已更新
- 面向用户的文档已更新,如适用
Feature Flag 策略
通过 feature flag 发布,让部署与发布解耦:
// Feature flag check
const flags = await getFeatureFlags(userId);
if (flags.taskSharing) {
// New feature: task sharing
return <TaskSharingPanel task={task} />;
}
// Default: existing behavior
return null;
Feature flag 生命周期:
1. DEPLOY with flag OFF → Code is in production but inactive
2. ENABLE for team/beta → Internal testing in production environment
3. GRADUAL ROLLOUT → 5% → 25% → 50% → 100% of users
4. MONITOR at each stage → Watch error rates, performance, user feedback
5. CLEAN UP → Remove flag and dead code path after full rollout
规则:
- 每个 feature flag 都有 owner 和过期时间
- 全量发布后 2 周内清理掉 flag
- 不要嵌套 feature flag,否则组合会指数增长
- 在 CI 中同时测试 flag 开和关两种状态
分阶段发布
发布顺序
1. DEPLOY to staging
└── Full test suite in staging environment
└── Manual smoke test of critical flows
2. DEPLOY to production (feature flag OFF)
└── Verify deployment succeeded (health check)
└── Check error monitoring (no new errors)
3. ENABLE for team (flag ON for internal users)
└── Team uses the feature in production
└── 24-hour monitoring window
4. CANARY rollout (flag ON for 5% of users)
└── Monitor error rates, latency, user behavior
└── Compare metrics: canary vs. baseline
└── 24-48 hour monitoring window
└── Advance only if all thresholds pass (see table below)
5. GRADUAL increase (25% -> 50% -> 100%)
└── Same monitoring at each step
└── Ability to roll back to previous percentage at any point
6. FULL rollout (flag ON for all users)
└── Monitor for 1 week
└── Clean up feature flag
发布决策阈值
用这些阈值来决定每一步是继续推进、暂停观察还是回滚:
| 指标 | 继续推进(绿) | 暂停并调查(黄) | 立即回滚(红) |
|---|---|---|---|
| Error rate | 与 baseline 相比在 10% 以内 | 高于 baseline 10-100% | 超过 baseline 2 倍 |
| P95 latency | 与 baseline 相比在 20% 以内 | 高于 baseline 20-50% | 高于 baseline 50% 以上 |
| Client JS errors | 没有新增错误类型 | 新错误出现在 <0.1% 的会话中 | 新错误出现在 >0.1% 的会话中 |
| Business metrics | 持平或更好 | 下降 <5%,可能只是噪声 | 下降 >5% |
何时回滚
出现以下任意情况就立即回滚:
- 错误率超过 baseline 的 2 倍
- P95 延迟上升超过 50%
- 用户反馈问题突然激增
- 发现数据一致性问题
- 暴露出安全漏洞
监控与可观测性
需要监控什么
Application metrics:
├── Error rate (total and by endpoint)
├── Response time (p50, p95, p99)
├── Request volume
├── Active users
└── Key business metrics (conversion, engagement)
Infrastructure metrics:
├── CPU and memory utilization
├── Database connection pool usage
├── Disk space
├── Network latency
└── Queue depth (if applicable)
Client metrics:
├── Core Web Vitals (LCP, INP, CLS)
├── JavaScript errors
├── API error rates from client perspective
└── Page load time
错误上报
// Set up error boundary with reporting
class ErrorBoundary extends React.Component {
componentDidCatch(error: Error, info: React.ErrorInfo) {
// Report to error tracking service
reportError(error, {
componentStack: info.componentStack,
userId: getCurrentUser()?.id,
page: window.location.pathname,
});
}
render() {
if (this.state.hasError) {
return <ErrorFallback => this.setState({ hasError: false })} />;
}
return this.props.children;
}
}
// Server-side error reporting
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
reportError(err, {
method: req.method,
url: req.url,
userId: req.user?.id,
});
// Don't expose internals to users
res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: 'Something went wrong' },
});
});
发布后验证
上线后的第一个小时内:
1. Check health endpoint returns 200
2. Check error monitoring dashboard (no new error types)
3. Check latency dashboard (no regression)
4. Test the critical user flow manually
5. Verify logs are flowing and readable
6. Confirm rollback mechanism works (dry run if possible)
回滚策略
每一次部署发生之前,都必须先有回滚方案:
## Rollback Plan for [Feature/Release]
### Trigger Conditions
- Error rate > 2x baseline
- P95 latency > [X]ms
- User reports of [specific issue]
### Rollback Steps
1. Disable feature flag (if applicable)
OR
1. Deploy previous version: `git revert <commit> && git push`
2. Verify rollback: health check, error monitoring
3. Communicate: notify team of rollback
### Database Considerations
- Migration [X] has a rollback: `npx prisma migrate rollback`
- Data inserted by new feature: [preserved / cleaned up]
### Time to Rollback
- Feature flag: < 1 minute
- Redeploy previous version: < 5 minutes
- Database rollback: < 15 minutes
常见自我安慰
| 自我安慰 | 现实 |
|---|---|
| “在 staging 能跑,生产肯定也能跑” | 生产环境的数据、流量模式和边界条件都不一样。部署后还必须继续监控。 |
| “这个功能不需要 feature flag” | 每个功能都值得有一个 kill switch,再简单的改动也可能出问题。 |
| “监控是额外开销” | 没有监控,就只能靠用户投诉来发现问题,而不是靠仪表盘。 |
| “监控之后再加” | 必须在发布前加。你看不见的东西,就很难调试。 |
| “回滚像是在承认失败” | 回滚是负责任的工程行为。真正的失败,是把坏功能继续留在线上。 |
危险信号
- 没有回滚方案就部署
- 生产环境没有监控或错误上报
- Big-bang 发布,一次全开,没有 staging
- Feature flag 没有 owner,也没有过期时间
- 上线后的第一个小时没人盯发布
- 生产环境配置靠记忆而不是靠代码或配置管理
- “周五下午了,发了吧”
验证
部署前,确认:
- 发布前检查清单全部完成
- Feature flag 已配置,如适用
- 回滚方案已记录
- 监控仪表盘已准备好
- 团队已收到部署通知
部署后,确认:
- Health check 返回 200
- 错误率正常
- 延迟正常
- 关键用户路径可用
- 日志正常流动
- 回滚机制已测试或确认随时可用