跳至主要内容
一道埋点题,能问出三年经验

一道埋点题,能问出三年经验

我常用一道很小的题开场:给内容 App 的收藏功能设计埋点,要能算「收藏率」和「收藏后回访率」。

候选人通常会立刻开始报字段名:user_id、content_id、event_time。写到第四个就卡住了。这不是记忆问题,是他还没意识到埋点方案的产出物不是一张字段表,而是一份关于「什么算一次」的定义。做过一段时间的人会先反问:收藏率的分母是什么。

本系列第 2/10 篇 · 上一篇:指标涨了,为什么我不算它变好 · 下一篇:数据跌了三成,先别急着归因业务

分母比分子难定义

「收藏率 = 收藏次数 / 曝光次数」,这句话里最不可靠的是分母。

什么叫一次有效曝光?卡片在列表里滑过算不算,露出一半算不算,停留在屏幕上不足一秒算不算,同一张卡片来回滑三次算几次。这些不定下来,不同端的实现各不相同,iOS 说 3 次、Android 说 1 次,报表上的收藏率差异就纯粹是客户端差异。我一般写一条能测的规则:可见面积超过一半、停留超过一秒、同一内容同一次会话内只计一次曝光,并且把「会话」的切割规则(例如 30 分钟无操作)一并写进字典。

分子也要分两层。用户点了收藏按钮,是意图;服务端把关系写进收藏表,是结果。中间可能失败:网络超时、内容已下架、登录态过期触发了登录弹窗、按钮被连点两次。如果只埋前端点击,收藏率会把失败和取消都算成成功;如果只埋服务端落库,又看不到有多少人在登录弹窗那一层流失了。所以至少两个事件:

事件 触发方 时机 关键属性
content_exposure 客户端 满足有效曝光条件 content_id、位置、列表类型、实验分组
favorite_click 客户端 点击收藏按钮 content_id、当前收藏状态、来源位
favorite_success 服务端 关系落库成功 content_id、user_id、落库时间
favorite_remove 服务端 取消收藏成功 content_id、user_id、取消原因(若可得)

「收藏后回访率」还要补一个行为事件:用户在之后的 N 天内再次打开这篇内容。这里最容易漏的是归因窗口——7 天还是 30 天,从收藏时刻算还是从次日零点算,以及「回访」是打开详情页还是任意一次有内容的消费。窗口不定,两个数就没法比。

一份能落地的事件字典长什么样

事件名、属性、时机之外,我会要求答案里出现下面这些字段,缺一个就要多追问一句。

JSON21 行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"event": "favorite_click",
"event_id": "客户端生成的 UUID,用于全链路去重",
"user_id": "登录后 ID,为空时填 anonymous_id",
"anonymous_id": "设备级匿名 ID",
"session_id": "按 30 分钟不活动切分",
"event_time": "客户端发生时间,毫秒",
"recv_time": "服务端接收时间,用于判断延迟与补报",
"props": {
"content_id": "内容 ID",
"position": "列表位置",
"from": "信息流 / 详情页 / 搜索",
"exp_group": "曝光时刻的实验分组快照"
},
"common": {
"app_version": "客户端版本",
"os": "系统",
"channel": "渠道",
"network": "网络类型"
}
}

三个细节值得单独说。

一是幂等。客户端为了不丢数据会缓存重试,弱网下同一条事件可能上报两次;离线补报还会带来 event_time 和 recv_time 差好几个小时的数据。所以要有稳定的 event_id,服务端按它去重,计算侧约定按 event_time 归日、按 recv_time 判断迟到窗口。分不清这两个时间,回刷历史分区时一定出乱子。

二是实验分组必须快照。很多人把 exp_group 留空,想着分析时再关联用户当前分组。可用户可能中途被重新分流,或者实验已经下线,事后关联出来的分组和曝光当时根本不是一回事。分组要在事件发生时写进去,这是本系列第 4 篇谈实验时最常翻车的地方。

三是公共属性下沉。版本、端、渠道这类字段如果让每条业务事件各自定义,三个月后就会出现 app_ver、version、clientVersion 三种写法。它们应该由 SDK 统一注入,业务同学只填业务属性。

上线之前的验证,比方案本身更见功力

方案写完我会追问最后一句:你怎么知道它真的生效了。

只回答「看控制台有没有日志」的,通常没跟过一次完整的埋点上线。我的做法是拿一台真机,从曝光开始走到落库,把这条链路上的每一个事件逐字段打出来对一遍,包括退出重进、断网重试、切换账号、内容下架这几种情况。然后做一次对账:前端上报的 favorite_success 条数与服务端收藏表的当日新增行数比一比,算出上报率。对不上就先别发布报表,因为一个安静丢掉 5% 事件的埋点,比没有埋点更危险。

再往外一层是治理。事件字典要有 owner、字段类型、必填校验和版本号;新增字段可以加,已有字段不允许悄悄改语义;字段要下线得先查血缘,看哪些报表和实验还在读它。我见过最贵的失误是有人把 position 从「列表序号」改成「频道编号」,没人通知,于是所有按位置做的漏斗分析在那一刻起全错了,而没有任何报错。

面试里能主动谈到「取消收藏」「内容下架」「补报去重」的人,多半真被这些数据坑过。至于业务方说「干脆把所有点击都埋上」,我一般会问回去:这一条事件,未来要回答哪个问题?答不上来的,就不埋。埋点不是越多越好,它是要长期维护的接口,多一个字段就多一份以后没人敢删的债。

写完这份字典,我会先拿一台真机跑通从曝光到落库的整条链路,再允许它进报表。

关于本文

由 Lu's Blog 撰写,采用 CC BY-NC 4.0 许可协议。

#埋点 #数据产品 #面试

评论