客户端记意图,服务端记事实;两边对不上时,先信服务端。
一、少了一截的注册数
陈默的发票工具「零票」在十月第二周做了一次注册流程改版。上线三天,注册数从日均 38 掉到 31,付费转化也跟着掉了两成。他第一反应是改版把用户吓跑了,连夜把四步引导退回两步,数字没有回来。
他又去查服务端的账号创建记录,发现那三天每天仍然新建 37 到 39 个账号,和改版前一样。少掉的不是用户,是上报。一位早期用户在反馈邮件里提到自己装了拦截扩展,顺手把分析域名也加进了名单。陈默用无痕窗口复现了一遍:注册成功,页面跳转正常,只有上报请求在开发者工具里变成了一条红色的 blocked。
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
他把最近 30 天的客户端注册事件和服务端账号表按天并排,发现差距稳定在 7% 到 9% 之间,不是改版那三天才有。也就是说,这个洞一直存在,只是改版让它跨过了他心里那条「看起来不对」的线。两天里他查了发版记录、CDN、登录接口和数据库,最后发现问题在一个他从没配置过的浏览器扩展名单里。
客户端记意图,服务端记事实。 客户端能告诉你「他想注册」,服务端才能告诉你「账号真的建出来了」。把两者混成一个数字,等于把意愿当成结果,而意愿会被拦截、离线、旧版本和手快的返回键改掉。
丢数据不会报错,只会让结论失真。 请求被拦截时,前端不抛异常,SDK 不弹窗,日志里只有一条被浏览器吞掉的网络错误。你看到的是数字变小,不是系统故障,所以第一反应永远是「产品变差了」。
埋点上线要像发版一样有门禁。 代码合并前跑测试,发版前跑灰度,埋点却常常是「点了两下,报表里有数,就算通过」。没有门禁的埋点,坏了只能靠人眼在几周后偶然发现,而那时你已经按错的数字做过一轮决定。
三处校验比一套 SDK 便宜。 换一个更贵的采集工具,解决不了拦截与离线;三道各半小时的检查,能把「有没有到、到得全不全、两侧对不对」变成每次发布都能回答的问题。
二、二十四等于十四加十
二十四个事件的第一个分法不是按页面,也不是按代码在哪边跑,而是按「争议发生时谁能给出不可争辩的证据」。按这个标准分完,客户端十四、服务端十。
| 侧 | 事件数 | 记什么 | 典型失败 |
|---|---|---|---|
| 客户端 | 14 | 曝光、点击、步骤推进 | 拦截扩展、离线、旧版本缓存 |
| 服务端 | 10 | 注册成功、订阅变更、退款、密钥操作 | 重试重复、异步漏写、时钟偏差 |
客户端十四的共同点是「只有前端知道用户看到了什么」:页面曝光、按钮点击、引导走到了第几步、搜索框里输入了什么。这些事件丢了只影响体验分析,不会让收入结论出错。
服务端十的共同点是「都对应一次状态变更」:注册成功、订阅变更、退款、密钥创建与吊销,全部由数据库写入或支付回调确认。用户关掉页面之后这些事仍会发生,所以只有服务端能证明它们发生过。
十四加十而不是十二加十二,是因为两侧的失败模式不对称。曝光类事件服务端根本收不到,硬放过去只会得到一片零;结果类事件放客户端,则会被 8% 的丢失率污染,而收入数字不能带 8% 的误差。
还有一个附带好处:每个事件只有一个「权威侧」,对账时不用争论听谁的。双端都能发的事件只保留一侧计数,另一侧只用来做校验,不进入指标。
三、三处校验
三道检查分别放在开发、发布前、发布后。任何一道不过,就不往下走;这不是流程洁癖,而是埋点的错误没有异常栈,只能靠外部检查暴露。
| 时机 | 检查项 | 判据 | 不通过怎么办 |
|---|---|---|---|
| 开发环境 | 事件只进测试库 | 生产库出现测试事件 0 条 | 阻断合并,检查密钥与开关 |
| 上线前 | 3 条用例各触发一次 | 3 条关键事件在 5 分钟内可见,必填属性齐全 | 回滚本次发版,修好再上 |
| 上线后 24 小时 | 量与属性回归 | 与上周同日差异小于 10%,必填属性缺失率低于 1% | 看板标红,暂停用该指标做结论 |
开发环境这一关的关键不是「记得切回来」,而是让切不回来。测试密钥写进构建配置,生产构建里根本不存在测试项目的写入权限;这样即使有人忘了关开关,事件也进不了生产库。
上线前的三条用例固定为注册、一个核心动作、一次付费或退款。每条只要求一件事:在 5 分钟内出现在报表里,且六个必填属性都有值。5 分钟是人的耐心上限,超过它通常不是网络慢,而是队列积压、映射表写错或密钥指向了另一个项目。
上线后 24 小时的回归看两件事:量和属性。量对但属性缺,等于数字在、解释不了;属性全但量掉了三成,通常是发布把某个触发点弄丢了。两件事都过,才允许把这个事件写进当周结论。
三处校验的分工很清楚:开发环境防污染,上线前防断链,上线后防静默漂移。三处都做,一次发布多花约半小时;三处都不做,一次事故平均花掉两个工作日。
四、复算:8% 与 12 小时
把两侧的丢失率、差异和核对成本放在一起算一遍,才能判断这套校验值不值得。
客户端丢失 8%(拦截扩展、离线、旧版本未更新)
服务端丢失 0.5%(异步漏写、重试失败)
14 个客户端事件 × 8% ≈ 1.1 个事件类别的量系统性偏低
另有 1 个双端重复上报点,只保留一侧计数、另一侧只做校验
月事件 12,960 × 8% ≈ 1,037 条
同一动作两侧计数差 3.2%(以导出为例)
客户端 30 次、服务端 31 次
(31 - 30) ÷ 31 = 3.2%
event_id 去重后重复率 0.3%
12,960 × 0.3% ≈ 39 条/月
上线回归:每 2 周 1 次 × 30 分钟
= 1 小时/月 = 12 小时/年
对比一次事故排查:2 天 = 16 小时
| 观测项 | 正常带宽 | 超限先查 |
|---|---|---|
| 两侧同动作计数差 | 3% 到 5% | 拦截名单、离线补传、旧版本 |
| event_id 重复率 | 0.3% 以下 | 重试策略、离线队列 |
| 关键事件 5 分钟可见 | 3/3 | 队列积压、映射表、密钥 |
8% 与 0.5% 差的不是精度,是方向。 服务端的 0.5% 接近随机噪声,少的那几条不会系统性地偏向某类用户;客户端的 8% 是按人群聚集的:装拦截扩展的人、网络差的人、半年不更新版本的人,恰好是同一批。用客户端数字做同比,等于每次都在同一个方向上低估。
1.2 个事件类别是一个月约 1,037 条。 十五个上报点各低 8%,合起来相当于一个中等事件类别的整月量凭空消失。这个量级不会让看板归零,只会让每条曲线都低一点,而低得足够均匀,均匀到你以为是产品在缓慢变化。
3.2% 是可以解释的差。 客户端三十次、服务端三十一次,多出来的一次通常来自重试或用户双击。落在 3% 到 5% 的带宽内,记一笔就够;超过 5%,先按版本号拆开看,再查当天的拦截名单与离线队列。
0.3% 的重复率意味着每月约 39 条。 这个数字单独看很小,但如果漏斗分子只有几百,39 条足以把某一步的转化率抬高几个百分点。event_id 的作用不是让数据变干净,而是让重复可被识别、可被扣掉。
12 小时一年,比一次事故便宜。 每两周半小时,一年十二小时;陈默那次排查花了两天,十六小时。也就是说,只要这套核对在一年里帮你提前发现一次问题,时间就已经赚回来了,剩下的都是净收益。
跨版本埋点时,旧版本客户端会继续上报旧字段。 你把 plan 改名成 plan_code,三个月前的安装包还在发 plan,字典必须容得下并存:服务端同时接受两个字段至少两个版本周期,读的时候做一次合并,写的时候只写新字段。最危险的动作是在同一次发版里既改字段名又删旧字段,那会让旧版本用户静默掉出所有分群。
五、行动清单与复算验收
这八条按「先分侧、再校验、最后对账」的顺序做,一条一天也能在一周内落地。
- 把二十四个事件按「谁能给出事实」分成客户端十四与服务端十,写进字典的归属列。
- 给每个事件标一个权威侧;双端事件只保留一侧计数,另一侧只做校验。
- 所有事件带
event_id,服务端按它做幂等去重,重复不再进入指标。 - 开发环境使用独立项目与独立密钥,生产库出现测试事件即阻断合并。
- 上线前跑三条用例:注册、一个核心动作、一次付费或退款,5 分钟内可见。
- 上线后 24 小时做一次回归:比对量与必填属性缺失率,差异超过 10% 标红。
- 每两周花 30 分钟对一次两侧计数,差异超过 5% 必须写下原因。
- 字典为旧字段保留至少两个版本周期的兼容读法,新代码只写新字段。
验收标准:任意一个关键事件都能在两侧对上,且差异可解释;解释不了的那一条,不许进周报。
使用边界与实操验收
本文不覆盖指标怎么定义。 哪些数字进周报、分子分母怎么写、同名指标怎么区分,是下一篇的内容。本篇只解决「事件有没有完整地从两端到达」,假设你已经有一张确定的事件字典。
不要试图绕过拦截。 把上报伪装成页面资源、改用第一方域名、在本地缓存后反复重发,这些做法有的会失效,有的会带来合规风险。正确姿势是承认客户端会丢,把结果类事件交给服务端,而不是把丢失率藏起来。
丢失率要自己测,不要抄。 8% 是一个用于复算的假设值,不同产品、不同地区、不同用户群差别很大。测法很简单:选一个两侧都有的事件,按天并排两周,差异的中位数就是你的基线。
验收看两侧,不看单侧。 合格的标志不是客户端事件数好看,而是同一个动作在两侧都能找到,且差额能用一个具体原因解释。只有一侧有数的事件,可以看趋势,不能用来算钱。
关键参考与核验范围
核验日期:2026-10-26。以下链接用于核对客户端与服务端的采集边界、事件契约与重复数据处理;本文的十四加十拆分、8% 与 0.5% 丢失率、3.2% 差异与 0.3% 重复率均为本系列基线的教学演示。
- Collecting Data on the Client or Server | Twilio :用于核对两类采集各自的适用边界与取舍。
- Protocols Tracking Plan | Twilio :用于核对以 tracking plan 固定事件名、属性与版本的做法。
- Events - Docs - PostHog :用于核对事件定义、默认属性与去重的基本概念。
- Handling Duplicate Data | Twilio :用于核对重试与离线补传造成重复时的处理思路。
引用口径。 外部链接只用于确认通用概念与采集边界,不引用其产品价格、配额或具体 SDK 行为;文中的事件数、丢失率、差异与时长均来自本系列统一基线,或由基线在本篇内四则运算得出。
核验范围。 未实测任何浏览器拦截扩展的拦截比例,也未核验任何分析服务的采样、保留与去重实现。8% 与 0.5% 是用于复算的假设值,不是行业基准。
如何自行复核。 任选一个关键事件,按天并排客户端计数与服务端计数,连续看十四天:差异稳定在 5% 以内说明链路健康;某一天突然拉开,先查当天发版与版本分布,再查拦截名单与离线队列。
系列导航
- 上一篇:事件字典:二十四个事件、六个属性与一张变更表
- 下一篇:指标口径:十二个指标的五个要素与八个同名不同义
- 回到目录:一个人的产品度量:事件字典、指标口径与决策看板
- 相关阅读:崩溃上报与版本遥测:发布后 72 小时的止损阈值
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。