生产就绪检查清单
每个条目都说明了需要检查的内容。根据项目阶段,应用适合的条目,阶段层级定义在 CLAUDE.md 中;如果未定义,则推断项目阶段(原型、MVP、生产或扩展)并陈述。在审查中,将不适合当前阶段的条目标记为“暂不适用”,并说明使其变得相关的触发条件。
1. 安全与访问控制
- 授权: 在每个端点和每个访问的对象上检查服务器端权限,而不仅仅是在登录时(防止 IDOR 和 BOLA)。如果使用 Supabase、Firebase 或类似后端,必须启用并测试行级安全或安全规则。
- 输入验证: 在每个信任边界使用模式库(Zod、Pydantic 或等效库)验证类型、长度、格式和范围。客户端验证仅用于用户体验,绝不可用于安全。
- SQL 注入: 仅使用参数化查询或 ORM 绑定。对动态标识符(如排序列)使用白名单。
- XSS: 依赖框架转义。避免将
innerHTML、dangerouslySetInnerHTML和v-html与不可信数据一起使用,或者使用经过验证的库(如 DOMPurify)进行清理。添加内容安全策略(CSP)。 - CSRF: 对于基于 cookie 的会话,使用 SameSite cookie 以及 CSRF 令牌或对状态更改请求进行来源检查。切勿在 GET 请求上更改状态。
- CORS: 对精确来源使用白名单。切勿反射任意来源或允许带凭据的通配符。CORS 不是访问控制。
- SSRF: 当服务器获取用户提供的 URL 时,对目标使用白名单,阻止 IPv4 和 IPv6 的私有地址、回环地址和链路本地地址(包括
169.254.169.254处的云元数据),并在重定向和 DNS 解析后重新检查。 - 机密管理: 从环境变量或机密管理器加载机密。它们绝不出现在代码、日志、容器镜像或 git 历史中,任何泄露的机密都需要轮换。
- IAM: 对每个人、服务和令牌应用最小权限原则。每个环境和每个服务使用独立的凭据,绝不在应用程序中使用 root 或管理员密钥。
- OAuth: 使用经过验证的提供商或库,而不是自定义身份验证。对浏览器和移动客户端使用授权码加 PKCE,精确匹配重定向 URI,验证状态,并使用最小范围。
- JWT 轮换: 使用短期访问令牌,轮换刷新令牌并检测重复使用,通过密钥 ID 和 JWKS 轮换签名密钥。始终验证签名、算法、签发者、受众和过期时间。
- 密码和会话安全: 使用 Argon2id 或 bcrypt 哈希密码。将会话 cookie 设置为 HttpOnly、Secure 和 SameSite。在账户持有价值的地方提供 MFA。
- TLS: 在所有地方使用 HTTPS,自动证书续订,最低 TLS 1.2(首选 1.3),并启用 HSTS。切勿禁用证书验证。
- 传输中加密: 加密内部流量以及公共流量,包括服务、数据库、缓存和队列连接。
- 静态加密: 加密数据库、磁盘、备份和对象存储,密钥存储在 KMS 中。对高度敏感数据考虑字段级加密。
- 安全头部: 设置 Content-Security-Policy(包括
frame-ancestors,以防止点击劫持)、Strict-Transport-Security、X-Content-Type-Options: nosniff和 Referrer-Policy。 - WAF: 在公共生产应用前放置具有托管规则集的 Web 应用防火墙。
- DDoS 防护: 依赖提供商保护(Cloudflare、AWS Shield 或平台默认设置)以及速率限制和自动扩缩容上限,以防止攻击导致无上限的账单。
- 供应链安全: 在 CI 中运行依赖漏洞扫描(npm audit、pip-audit、Dependabot 或类似工具)。验证每个新包是真实的、名称正确且维护良好的。
- 文件上传: 根据内容而非扩展名检查类型,限制大小,以随机名称存储在对象存储中,并通过短期签名 URL 提供私有文件。
- 审计日志: 记录敏感操作(身份验证事件、权限更改、数据导出、资金移动)的操作者、操作内容和时间,存储在仅追加的存储中。
- 隐私与 PII: 收集最少数据,了解个人数据的位置,避免将其放入日志,定义保留期限,并在 GDPR 或 CCPA 等法律适用时支持删除和导出。
2. 数据与数据库
- 数据库索引: 在将增长的表中,对用于过滤、连接和排序的列(包括外键)建立索引。避免冗余索引,因为它们会减慢写入速度。
- 查询优化: 对慢速或频繁查询使用
EXPLAIN检查计划。仅选择需要的列,并避免在 WHERE 子句中对索引列使用包装函数。 - N+1 查询: 发现每次查询一个项目的循环。用连接、预加载或批处理替换它们。
- 连接池: 根据数据库限制调整池大小。在无服务器环境中,使用连接池器(PgBouncer、RDS Proxy 或提供商的池器),因为每个实例都会打开自己的连接。
- 事务: 将必须全部成功或全部失败的多步写入包装在一个事务中,并有意识地选择隔离级别。
- 约束: 在数据库中通过 NOT NULL、UNIQUE、外键和 CHECK 约束强制完整性。仅应用程序级别的检查无法应对竞态条件。
- 分页: 对每个列表端点和查询进行分页,并限制页面大小。对于大型或变化的数据,首选游标(键集)分页。
- 货币与时间: 将货币存储为整数最小单位或十进制类型。将时间戳以 UTC 存储,仅在显示时转换。
- 只读副本: 仅在读取过载主库时添加,并处理复制延迟,特别是当用户读取自己的写入时。
- 复制: 了解复制是同步还是异步,以及故障切换可能丢失多少数据。
- 分区: 对非常大的表(通常按时间)进行分区,以保持索引和可维护性,并通过删除分区来过期旧数据。
- 分片: 将其视为索引、缓存、副本和垂直扩展之后的最后手段。选择避免热点和跨分片查询的分片键。
- 数据库迁移: 保持迁移版本化、经过审查、在部署中自动化,并尽可能可逆。对于零停机,使用扩展和收缩:在后续版本中添加、回填、切换,然后删除。在进行破坏性更改前备份。
- 模式版本控制: 对数据契约(数据库模式、事件负载、API 模式)进行版本控制,以便在部署期间新旧代码可以并行运行。
3. 并发与分布式系统
- 竞态条件: 寻找“检查然后执行”的代码(余额、库存、唯一用户名、优惠券兑换)。使用原子更新、约束、事务或锁来修复。
- 线程安全: 同步或消除线程、工作者和异步任务之间的共享可变状态。模块级全局变量通常跨请求共享。
- 乐观锁: 在低争用记录的更新时检查版本列或 ETag,并在冲突时返回 409 或 412。
- 悲观锁: 对高争用关键部分使用
SELECT ... FOR UPDATE,并保持锁的持有时间短。 - 死锁: 以一致顺序获取锁,保持事务简短,并在死锁错误时重试。
- 分布式锁: 仅用于跨进程的协调,使用具有过期时间和 fencing 令牌的租约。当数据库约束或队列足以满足时,优先使用它们。
- 分布式事务: 避免跨服务的两阶段提交。尽可能将事务边界保持在一个数据库内。
- Saga 模式: 对于跨服务的工作流,使用补偿操作链接本地事务,并使每一步都幂等。
- CAP 定理: 在网络分区期间,系统必须选择一致性或可用性。根据特性决定:余额和库存需要一致性,而订阅源和计数器可以容忍陈旧数据。
- 最终一致性: 在可接受的情况下,为陈旧读取设计 UI 和逻辑,并使更新收敛。
- 领导者选举: 当必须恰好一个实例执行操作时(调度器、单例工作者),使用租约或平台原语,绝不要使用硬编码实例。
- 网络分区: 假设任何调用可能中途失败或成功但无响应。使用超时、幂等性和协调来设计。
- 时钟偏差: 绝不能依赖跨机器的时钟进行排序或正确性。使用数据库时间戳、序列号或逻辑时钟,并在令牌过期检查中允许小幅度的偏差。
- 服务发现: 通过 DNS、平台或注册表解析服务,绝不要使用硬编码 IP。
4. 弹性与容错
- 超时: 为每次出站调用(HTTP、数据库、缓存、队列、LLM API)设置连接超时和请求超时,使其适合调用方自己的截止时间。
- 重试: 仅对瞬时故障(超时、连接错误、429、502、503、504)在幂等操作上重试,并限制尝试次数。避免同时在每个层重试,这会成倍增加负载。
- 指数退避: 使用指数退避并加入抖动,并遵循
Retry-After头部。 - 断路器: 在重复错误后停止调用失败依赖项,快速失败并提供回退,然后探测恢复。
- 幂等性: 为可能重试的操作(支付、订单、Webhook 处理器、作业)提供幂等键或自然键,使重复操作不会产生额外影响。
- 背压: 限制队列、缓冲区和并发。当过载时,丢弃负载或返回 429 或 503,而不是接受无限制的工作。
- 隔舱: 隔离资源(独立的池或工作者),使一个慢速依赖项不会耗尽所有资源。
- 优雅降级: 决定当依赖项宕机时哪些功能仍然可用,例如缓存数据、只读模式或暂时隐藏某个特性。
- 优雅关闭: 收到
SIGTERM后,停止接受新工作,完成处理中的请求和作业,然后关闭连接。 - 健康检查: 为负载均衡器或平台暴露一个轻量级的健康端点。
- 存活与就绪探针: 存活探针仅检测进程卡住的情况,不得依赖外部服务,否则一个数据库小故障就会重启所有内容。就绪探针决定实例是否应接收流量,并可检查依赖项。
- 故障切换: 了解当主数据库、可用区或提供商故障时会发生什么。在重视正常运行时间的情况下自动化故障切换,并测试它。
5. 消息传递、后台工作与实时
- 消息队列: 将慢速、可重试或突发的任务(电子邮件、媒体处理、第三方调用)移出请求路径,放入带有工作者的队列中。
- 传递语义: 假设至少一次传递,因此消费者必须是幂等的。除非系统保证每个键的顺序,否则不要假定顺序。
- 死信队列: 将耗尽重试次数的消息路由到死信队列,并设置告警和重放路径。绝不能静默丢弃或无限重试。
- 发布/订阅: 用于扇出到多个独立消费者,并定义事件模式和所有权。
- 事件驱动架构: 当解耦的收益超过调试成本时采用。将事件视为版本化契约,并保持跨事件的请求可追溯。
- 事务性发件箱: 当数据库写入必须同时发布事件时,在一个事务中写入两者(发件箱表)并异步中继,避免双重写入问题。
- Cron 作业: 使计划作业幂等、安全地允许运行两次或重叠、每次仅在一个实例上运行,并监控遗漏或失败的运行。
- WebSocket: 在连接时进行身份验证,发送心跳,使用退避重新连接,并规划多个实例(共享发布/订阅或粘性会话)。
- 服务器发送事件: 对于从服务器到客户端的单向流(如通知、进度和 LLM 令牌流),优先使用 SSE 而不是 WebSocket。
- 长轮询: 在 WebSocket 和 SSE 不可用时作为后备方案,设置超时并限制保持的连接数。
- Webhook: 对于传入的 Webhook,验证签名(HMAC),拒绝陈旧的时间戳以防止重放攻击,快速确认并异步处理,并通过事件 ID 去重。对于传出的 Webhook,对负载签名,使用退避重试,并对事件格式进行版本控制。
6. 流量、边缘与网络
- 速率限制: 对公共、认证和昂贵端点(登录、注册、密码重置、OTP、LLM 调用)按用户、API 密钥和 IP 进行限制。返回 429 并附带
Retry-After。 - 负载均衡: 将流量分配到健康检查通过的实例。保持应用服务器无状态,以便任何实例都可以服务任何请求。
- 反向代理: 在代理处终止 TLS、压缩、并强制主体大小和超时限制。仅从已知代理信任
X-Forwarded-For,以防止客户端 IP 被伪造。 - API 网关: 当有多个服务或公共 API 时,集中身份验证、速率限制、路由和验证。单一应用很少需要。
- CDN: 通过 CDN 提供静态资源和媒体,设置较长的缓存生命周期和使用内容哈希文件名。
- DNS: 在迁移前降低 TTL,尽可能将记录作为代码管理,并预期传播延迟。
- TCP vs UDP: 默认使用基于 TCP 的协议。仅对容忍丢失的延迟敏感流量(实时媒体、游戏)使用 UDP,并进行应用层处理。
- HTTP/2 和 HTTP/3: 在边缘或 CDN 启用它们以实现多路复用和更快连接。这通常是配置问题,而不是应用代码问题。
- gRPC: 用于需要严格契约或流式传输的内部服务调用,每次调用设置截止时间。浏览器需要 gRPC-Web 或网关。
7. 缓存
- 缓存: 仅在测量需要后进行缓存,并定义每个缓存的键、TTL 和受众。绝不要将用户特定或权限相关的数据存储在使用通用键的共享缓存中。
- 边缘缓存: 显式设置
Cache-Control,将个性化响应标记为private或no-store,在数据新鲜度允许的情况下使用stale-while-revalidate。 - 本地缓存: 用于每个实例的、快速变化的数据,但要注意使缓存失效,并注意内存限制和实例间的不一致性。
- 缓存失效: 在写入或事件触发时显式失效缓存。模式包括缓存旁路、读取穿透和写入穿透。基于 TTL 的失效适用于最终一致性可接受的数据。
8. 性能与 LLM
- 延迟预算: 定义用户可见操作的端到端延迟目标,并将预算分解到每个服务和调用。
- 预计算: 在请求时间之前计算或聚合结果,并存储以供快速检索。
- 异步处理: 将非关键路径工作(通知、日志、分析、报告生成)移到后台。
- 响应压缩: 使用 gzip 或 Brotli 压缩 API 和静态响应。
- 日志记录: 使用结构化日志(JSON),以避免阻塞 I/O。保持请求 ID 用于追踪。注意:日志记录本身不应成为性能瓶颈。
- 指标: 记录请求速率、错误率、延迟(按百分位数)、饱和度,如果可能的话进行分布式追踪。
- 性能测试: 在负载下测试新功能。使用冒烟测试进行快速检查,使用压力测试找到破坏点,使用耐力测试(稳定状态)检查泄漏和退化。
- LLM 令牌预算: 跟踪每个请求的提示和补全令牌,设置每个用户或密钥的限制,以避免账单冲击。
- LLM 流式传输: 可能时使用流式响应,以改善用户体验并更快开始显示输出。
- LLM 提示注入: 通过输入验证、输出过滤和清晰的角色边界来防范提示注入。不要完全相信模型输出。
- LLM 速率限制: 对 LLM API 使用严格的速率限制,并优雅地处理配额耗尽错误。
9. API 设计
- RESTful 命名: 对资源使用名词,对集合使用复数,对动作使用 HTTP 方法(GET、POST、PUT、PATCH、DELETE)。在出现非 CRUD 操作时保持一致性。
- 版本控制: 通过 URL 前缀(
/v1/)或请求头对 API 进行版本控制。永远不要在没有弃用期和迁移计划的情况下删除旧版本。 - 错误响应: 使用一致的模式结构返回错误,包括机器可读的错误代码、人类可读的消息和可选的详细信息。
- HTTP 状态码: 正确使用状态码:200 表示成功,201 表示创建,204 表示删除,400 表示错误请求,401 表示未授权,403 表示禁止,404 表示未找到,409 表示冲突,422 表示不可处理的实体,429 表示速率限制,500 表示内部错误。
- 分页、过滤和排序: 标准化用于列表端点的查询参数:
page、limit、sort、filter。对于大型数据集使用游标分页。 - OpenAPI / Swagger: 记录所有端点,包括请求和响应模式、身份验证要求和错误情况。
- GraphQL: 如果使用,实施按查询成本或深度的限制,禁用自省如果暴露模式,并使用数据加载器进行批处理。
- API 安全性: 对所有非公共端点使用身份验证。通过 HTTPS 发送密钥。实现速率限制并监控滥用。
10. 可观测性
- 结构化日志: 以机器可解析的格式(JSON)记录,包括时间戳、严重级别、服务名称、请求 ID 和上下文字段。避免在热路径中记录或记录敏感数据。
- 日志级别: 为开发使用 debug,为失败和异常使用 error,为有意义的状态变更使用 info,为高频率消息使用 warn。在 CI 中默认为 warn 或 error。
- 分布式追踪: 向依赖项传递追踪上下文(trace ID、span ID),以在服务间连接请求。使用采样来管理成本。
- 指标: 暴露 RED(速率、错误、持续时间)和 USE(利用率、饱和度、错误率)指标。
- 告警: 对错误率或延迟上升、服务宕机、关键队列变满、证书即将过期等设置告警。在凌晨 3 点无法操作的告警会损坏信任。配置通知路由。
- 仪表板: 维护关键服务的仪表板,显示请求、错误、延迟和依赖项健康状况。为运行手册添加指向仪表板的链接。
- 运行手册: 记录常见故障模式、诊断步骤和恢复程序。在事件中保持更新。
- 事件管理: 拥有严重性分类、通信渠道、事件指挥官和事后分析流程。进行事后分析以改进。
11. 测试
- 单元测试: 测试隔离的业务逻辑。为核心算法和边缘情况编写测试。快且确定。
- 集成测试: 测试跨边界(数据库、API、第三方)的真实交互。为每个外部依赖项编写测试。通常较慢,可能需要清理。
- 端到端测试: 测试传播关键路径的完整用户旅程。编写少量以保持可维护性。
- 契约测试: 验证服务遵守其 API 契约(OpenAPI、gRPC proto、事件模式)。用于在消费者驱动的开发中及早发现中断。
- 快照测试: 对 UI、API 响应和生成的文件使用,但在代码审查中仔细验证更改。
- 测试金字塔: 保持许多单元测试、适量的集成测试和少量 E2E 测试。
- CI 流水线: 在每次推送或合并请求时运行测试。运行 linting、安全扫描和类型检查。快速检测失败。
- 模拟: 模拟外部依赖项以隔离测试,使它们可靠和快速。编写验证行为的测试,而不是实现细节。
- 属性测试: 用于需要稳健性但难以手动枚举的算法和解析器。测试不变量而不是特定输出。
- 回归测试: 在修复 bug 时添加,以防止再次发生。从失败的测试开始。
12. 交付与 CI/CD
- 自动化构建: 构建应该是可重复的。锁定基础镜像、应用程序依赖项和工具。在 CI 中构建,而不是在本地。
- 不可变工件: 构建一次,通过环境提升。绝不修补或重新标记相同的版本。使用语义版本化或提交哈希。
- 部署策略: 选择适合风险承受能力的方法:滚动部署(默认,零停机)、蓝绿部署(存在切换回滚)、金丝雀部署(风险最高,最快速反馈)、功能标记(最安全,部署与发布分离)。
- CI/CD 流水线: 自动执行多个阶段:构建、lint、测试、安全扫描、构建镜像、部署到登台、集成测试、部署到生产。确保阶段之间的门控。
- 基础设施即代码: 以代码形式管理基础设施(Terraform、CloudFormation、Pulumi、Kubernetes 清单)。将其版本化、审查和部署。绝不要手动编辑云资源。
- 配置管理: 使用环境变量或密钥管理器管理配置。绝不要硬编码。为本地、登台和生产使用不同的配置。
- 秘密轮换: 定期自动轮换 API 密钥、数据库密码和证书。拥有失效流程。
- 零停机部署: 确保更新时用户不丢失请求。使用运行状况检查、蓝绿部署或滚动更新。
- 回滚: 拥有测试过的回滚计划。自动化快速回滚。如果需要构建步骤,确保它们可重复。
- 功能标记: 将部署与发布分离。使用功能标记逐渐、安全地公开功能。在切换到开或关后进行测试。
13. 灾难恢复
- 备份频率: 根据数据丢失容忍度安排备份:关键数据频繁备份(每小时、即时),其他数据较少备份。
- 备份验证: 定期测试备份的可恢复性。未经验证的备份与没有备份无异。
- 恢复点目标: 定义在灾难中可丢失的最大数据量。这决定了备份频率。
- 恢复时间目标: 定义恢复服务的最长时间。这决定了恢复过程。
- 地理冗余: 将备份和数据复制到不同地理区域。在区域灾难期间避免单点故障。
- 多区域部署: 对于高可用性应用,准备在另一个区域运行。自动进行故障切换。
- 混沌工程: 引入故障以测试系统。从模拟失败开始,逐渐增加范围。
- 事件响应: 拥有测试过的事件响应计划,清晰分配角色,如事件指挥官、通信负责人。在事件后进行回顾。
14. 客户端
- 错误处理: 以用户友好的方式优雅地处理错误。显示可操作的消息,而不是崩溃或错误代码。
- 离线支持: 在客户端本地存储数据,并在连接恢复时同步(本地优先架构)。
- 安全存储: 使用安全存储(Android 上的 EncryptedSharedPreferences、iOS 上的钥匙串、Web 上的 HttpOnly cookie)存储访问令牌、秘密和敏感用户数据。
- 加载状态: 在数据获取期间显示加载指示器或骨架屏幕。
- 输入验证: 验证用户输入以确保格式正确,但记住安全验证必须在服务器端进行。
- 性能优化: 优化客户端性能:代码分割、延迟加载、树摇和资产优化。
- 辅助功能: 设计具有良好对比度、语义 HTML 或原生 UI 元素、Alt 文本和支持键盘导航。
- 响应式设计: 创建适应不同屏幕尺寸和设备方向的布局。
- 本地化: 支持多种语言和地区格式,如果面向全球用户,包括从右到左的语言。
15. 安全清单
- 日志中无秘密: 确保日志语句不记录密码、令牌、API 密钥或个人身份信息。
- HTTPS 仅: 对所有通信强制使用 HTTPS。在开发中也进行测试。
- 无硬编码配置: 配置来自环境变量或机密管理器。绝不要硬编码。
- 依赖项检查: 定期使用
npm audit、pip audit或 Dependabot 检查依赖项中的已知漏洞。 - 定期安全审查: 将安全审查作为开发周期的一部分。在重大更改或启动前进行。
- 渗透测试: 在新版本或重大更改前,由专家或自动化工具进行渗透测试。
- 漏洞赏金: 拥有经过验证的用户的公共项目应考虑漏洞赏金计划。
- 安全联系人: 拥有一个公开的安全联系人邮件或流程,以便研究人员报告漏洞。
- 依赖项更新: 保持依赖项更新,安全补丁优先。自动进行,但具有自动化测试以确保不变坏。
一般原则
- 防御性编程: 假设前置条件、后置条件和不变量被违反。验证输入,检查 null 和边界情况,优雅处理错误。
- 最小惊讶原则: 选择这样设计的 API 和架构,首次阅读就能猜出使用方法。
- 不要重复自己: 提取公共逻辑、配置和模式。重复导致不同步,不同步导致错误。
- 凯特莱 (YAGNI): 你不一定会用到它。除非有足够的证据表明将来会需要,否则不要添加功能。
- 单一责任原则: 每个模块、函数和类应该有一个改变的理由。
- 开放/封闭原则: 软件实体应该对扩展开放,对修改封闭。
- 里氏替换原则: 子类应该可以替换基类而不改变正确性。
- 接口隔离原则: 客户端不应该被迫依赖于他们不使用的接口。
- 依赖反转原则: 依赖抽象,而不是具体实现。