原始工单
添加通知偏好设置。 用户应该能开启或关闭邮件和推送。 注意不要骚扰用户。
这个案例展示如何把“让用户管理通知”这种宽泛需求,改写成包含同意规则、老用户迁移、重复发送证据和 AI 编码护栏的规格包。
添加通知偏好设置。 用户应该能开启或关闭邮件和推送。 注意不要骚扰用户。
Feature: Notification preference migration Owner: Lifecycle Messaging Status: Ready for review Context: - 现有用户的 email_opt_out 存在 users 表。 - Push token 存在 device_tokens。 - 营销邮件已经遵守 unsubscribe 记录。 Goal: - 为产品邮件和推送建立统一偏好模型。 - 迁移时保留所有现有退订状态。 - 旧 reader 和新 reader 共存期间避免重复发送。 Non-goals: - 不做 SMS 偏好。 - 不重设计营销邮件。 - 不做后台批量编辑。 - 不新增通知历史页面。
已有退订保持关闭;没有显式退订的用户继续保持原投递行为,直到主动修改设置。
旧 reader 和新 reader 可以共存一个发布周期,但每类通知只能选择一个事实来源来决定是否投递。
本次只负责产品邮件和推送。营销退订规则和 SMS 同意状态不进入当前 diff。
Preference categories: - product_updates_email - product_updates_push - billing_alert_email - billing_alert_push Rules: - opt-out 记录迁移为 disabled preference。 - billing alert 本次不能关闭。 - push disabled 表示不再入队新的 push job。 - email disabled 表示 delivery worker 抑制发送。
- [ ] 新增 preference 表和回填脚本。 - [ ] 新增 settings UI 的读取 API。 - [ ] 新增带 audit 字段的更新 API。 - [ ] 更新 email worker 抑制逻辑。 - [ ] 更新 push enqueue 抑制逻辑。 - [ ] 添加 dual-read 安全测试。
- Given 已存在 email opt-out When preferences 被迁移 Then product_updates_email 是 disabled。 - Given 用户关闭 push When product update 事件触发 Then 不会为该用户入队 push job。
Required: - backfill dry-run count - opt-out preservation test - duplicate-send regression test - worker suppression test - settings UI screenshot - preference reader flag 回滚方案
第一轮评审看的是当前同意状态有没有被保留。设置页很容易生成,真正危险的是迁移:已有退订、device token、营销 unsubscribe 记录和产品提醒规则看起来很像,只有规格把它们逐一命名后,评审者才能知道谁负责哪一类行为。
第二轮评审看重复投递。迁移过程中,旧 reader 和新 reader 经常会并行运行。如果两边都入队消息,用户就可能对同一个事件收到两条通知。好的规格包要写明双读窗口、事实来源,以及证明只创建一个 job 的回归测试。
第三轮评审看回滚。关闭 UI 并不会撤回已迁移的 preference 行。规格包应该写明控制新 reader 的 flag,以及暂停发布后用于验证 opt-out 仍然保留的查询。
SMS、营销重设计、通知历史和后台工具都应拒绝进入当前 diff,除非它们有自己的规格。
要求提供 dry-run 数量、退订迁移 fixture、重复发送回归测试,以及一个能和 API 状态对应的 UI 截图。
发布应通过 reader flag 控制。停止信号是任何重复产品通知,或 preference API 与 worker 决策不一致。
只实现通知偏好迁移。 允许修改: - preference schema 和迁移 - settings API 读取/更新 - email worker 抑制 - push enqueue 抑制 - 验收标准对应的测试和 fixture 不要新增: - SMS consent - notification history - admin bulk editor - marketing email redesign - 无关 settings page 重构 每个改动文件都必须对应一个任务和一条验收标准。
这条护栏故意写得很窄。通知需求天然会吸引相邻改进:新渠道、更好看的设置页、后台覆盖、活动历史和分析看板。这些可能有价值,但放进同一次迁移会削弱评审,也会让同意状态更难审计。
当某个偏好、同意状态或设置变更会影响真实投递行为时,可以套用这个模式。把渠道名称替换成你的业务名称,然后先写旧系统的事实来源,再写新模型。如果当前系统里有 unsubscribe 行、device token、profile flag、团队级设置或地区同意规则,都要逐一列出来。不要让新 UI 成为规格里唯一被描述的东西。
小团队可以把规格包写得很短,但核心内容不能少:当前行为、迁移规则、双读规则、非目标和证据。少了任何一项,AI 实现都可能看起来很完整,却在没有评审的情况下改变同意语义。
大团队还应该加一个“责任归属”部分。产品消息、生命周期营销、账单提醒和移动推送往往有不同 owner。规格没有写清 owner,工程评审可能通过,但另一个团队的投递政策可能已经被破坏。
worker 必须执行偏好规则。设置开关如果没有进入投递链路,只会制造虚假的安全感。
改变老用户默认投递行为是产品和同意决策,必须写进规格,而不是藏在迁移脚本里。
没有 dry-run 数量和 mismatch 检查的迁移,无法证明它保留了当前退订状态。
通知偏好迁移最容易在发布后暴露问题,因为 UI 看起来正确并不等于投递链路真的遵守了偏好。规格包应该预先写好观察窗口。
在一个发布周期内观察同一事件是否同时由旧 reader 和新 reader 入队,发现重复 job 就停止 rollout。
抽样检查迁移前的退订用户,确认新 preference 行、API 响应和 worker 决策三者一致。
如果偏好迁移涉及表结构和回填,继续参考 数据库 Schema 变更案例;如果需求仍很宽,先看 模糊需求改写示例。
观察窗口结束前,不建议同时上线新的通知渠道或营销规则;否则很难判断问题来自迁移、旧逻辑还是新增范围。
这个案例基于通知迁移中常见的失败模式:同意状态漂移、重复发送、缺少回填证据,以及只在 UI 层检查偏好。