允许我上来先介绍一个好东西!
面试导航 是一个专注于前、后端技术学习和面试准备的 免费 学习平台,提供系统化的技术栈学习,深入讲解每个知识点的核心原理,帮助开发者构建全面的技术体系。平台还收录了大量真实的校招与社招面经,帮助你快速掌握面试技巧,提升求职竞争力。如果你想加入我们的交流群,欢迎通过微信联系:
Tongxx_yj
。
背景
埋点到底有多重要呢?严格意义来说,埋点适用于任何服务用户的场景,无论是 C 端、B 端业务,还是内部使用的工具链
那埋点能带来什么作用呢?通过埋点获取用户、平台的相关数据,可以帮助我们:
- 报警监控:如果观测到 xx 指标存在问题(不合实际、波动显著等),可触发报警让研发及时响应问题
- AB 实验,回收数据收益:通过相关的指标来认证 xx 需求是否带来了业务、技术收益(比如某次性能优化,减少了用户流失)
- 分析用户行为 :通过多项数据联动(指标下钻、漏斗分析等),观测用户行为
- 为应用优化提供更多思路
- 为数据分析提供有力证明
如果对埋点的概念还有不清晰的同学,可以前置学习一下:大家都该学学的埋点概念与使用😎
本篇文章将默认大家对埋点拥有基本认知,核心围绕埋点在复杂应用中的实践(尤其是 C 端业务),通过这篇文章,你可以学习到:
- 埋点的基本实践方案
- 复杂场景的埋点运用
埋点的基本实践方案
如果你有一定的埋点开发经验,可以直接跳过这一部分
首先我们给自己一个明确的身份:电商 C 端业务的一个研发同学
其次,我们明确要负责的业务场景:近期主要负责商品浏览记录的场景 ,此处参考"抖音商城=足迹"
再者,我们明确一下我们要做的事情:
- 需求:实现一个记录的商品浏览信息的页面
- 功能:用一个 list 展示商品信息,点击商品信息可跳转至对应商品
产品视角
OK,面对这样的一个简单需求,我们尝试从产品的角度对功能进行拆解:
功能点 | 详细说明 |
---|---|
浏览信息展示 | 页面初始化后,在页面渲染一个无限滚动 list,展示该用户的浏览商品记录 |
点击商品进行跳转 | 点击商品卡,页面会跳转至对应的商品详情页 |
明确功能之后,我们再从产品的角度来看看需要些什么数据(简单版本)
数据 | 指标描述 |
---|---|
页面曝光数 | 描述用户对足迹页面的使用频率和潜在关注度 |
商品曝光 | 描述用户进入页面后,某个商品的展示 |
商品点击 | 描述用户进入页面后,某个商品的点击 |
好了,至此一个简易版本 PRD 已经输出到了研发这边,需求评审要开始了
研发视角
需求评审阶段,我们得知了产品想要什么数据,那这个时候,我们就需要通过埋点来满足他们的诉求
我们先忽视页面搭建的过程(这太简单了),只关注埋点的设计、研发和验收
埋点设计
对于埋点的参数,我们默认系统会带上用户 id 等全局参数
我们基于产品的诉求来设计埋点,这种埋点大部分是和数据一一对应的,如:
数据 | 事件名称 | 事件参数 |
---|---|---|
页面曝光数 | page_view | - page_name,string,页面名称 - total_count,number,商品数量 |
商品曝光 | product_show | - page_name,string,页面名称 - product_id,string,商品 id |
商品点击 | product_click | - page_name,string,页面名称 - product_id,string,商品 id |
埋点开发
有了一个比较清晰的设定,我们用来开发就很方便了,直接在对应的场景调用埋点 sdk 的 API! 比如:
ts
handleProductClick = async (id: string) => {
sendEvent('product_click', {
page_name: 'foot_print',
product_id: id
})
// 处理业务逻辑
}
当我们调用 sendEvent 的时候,相关的事件就会进行上报,此时数据服务会记录相关的数据,我们可以通过埋点平台 or 数据库直接查询对应的数据内容
数据验收
我们直接跳过埋点自测(抓包看数据就行了)环节,假设这个时候需求已经上线了,产品要来找研发看数据了,这个时候我们可以给到我们的埋点事件设计,让产品自己看数(当然也可以研发包掉)
比如产品要看 id 为 xxxxx 的商品点击数据,可以通过以下的条件来筛选:
事件名 = product_click && page_name = foot_print && product_id = xxxxx
其实到这一步,整个需求就已经结束了,只要数据符合预期,产品就不会继续找咱研发的事了~
当然这只是一个最基本的实现流程,我们会在后面的板块中给出更难的场景
复杂场景的埋点运用
如果仅仅是掌握上一个板块的知识点,我们仅能做到勉强支撑一些埋点开发,在实际的工作中大概率是要踩坑的 在这一个板块,我们会基于更复杂的场景展开,带大家体验一下实际工作中的强度
背景问题一:庞大的业务团队
在这个背景下,可能存在一个埋点事件被多条业务线的同学使用的情况,这种情况下会带来什么问题?
- 数据混乱:
- 原因:比如
page_name
参数,如果用成别的页面的命名,就导致数据错报了 - 解决方案:一般会通过测试同学检查、设立报警监控等方式规避,但难以完全避免!
- 原因:比如
- 事件参数复杂,难以维护
- 原因:每个团队都可能往公共事件里面加入参数定义,导致公共事件的参数越来越冗长,对公共事件的更新会变的异常艰难
- 解决方案:非必要不删除参数,避免不可预估的影响;尽可能使用已有的参数
这两个大头的问题,其实也没有太好的解决方案,长期以往,研发同学将持续在心智负担较大的背景下开发埋点代码,那么这个时候,我们有什么比较好的解决方案呢?
方案一:新开事件
面对这种复杂的现状,我们可以将一些有特殊传参要求的业务场景开设独立的事件
比如我们的足迹场景中,商品卡片上有一个进入店铺的按钮,我们需要为进入店铺按钮,这个时候我们有两种事件命名方式:
- 通用维度 :
button_click
,记录所有的按钮点击事件 - 页面维度 :
footprint_button_click
,记录足迹页面下的所有按钮点击事件 - 独立维度 :
footprint_enrty_shop_click
,记录店铺按钮点击事件
选择 2、3 都是不错的选择,但各有优劣:
- 页面维度不够精准 ,但能较大限度 避免上述问题带来的影响,且开发比较方便(维护更多的事件肯定会更加麻烦的)
- 独立维度非常精准 ,能最大限度避免上述问题带来的影响,但是开发比较繁琐(要维护更多的事件了)
但综合来说,个人会更倾向使用页面维度,为什么?
我们把事件的数量比作 A
,需要使用的场景比作 B
,我们假设需求中,每个场景只需要埋一个
那么总共要埋的数量 X
= A * B
,可以理解为, A + B
是整个需求的复杂度(可以理解为研发要考虑的范围)
- 通用维度:
X
=1 * B1
- 页面维度:
X
=A2 * B2
- 独立维度:
X
=A1 * 1
可以得到 B1
= A2 * B2
,我们取几组数字带入看看:
- 左侧:2,右侧:2 * 1
- 左侧:4,右侧:2 * 2
- 左侧:6,右侧:2 * 3
- 左侧:16,右侧:2 * 8
可以看到,随着数据越来越大,右侧两数之和会越来越低于左侧,那研发的复杂度就会越来越低~
👇很重要 !!!!!!!!!!!!!!!!!!!!!!!!
ps:这是一种通用的思想!非常好用,面对复杂度很高的场景,通过拆分 & 叉乘的方式,来降低复杂度
👆很重要 !!!!!!!!!!!!!!!!!!!!!!!!
方案二:统一参数标准
统一参数标准,顾名思义,我们会对一些通用参数的使用做规范统一
举个例子,电商场景,我们通过一系列方式让用户下单,完成交易,那这个时候我们需要额外注重一个事情:
用户从哪个商品卡点到商品详情的
为什么要额外注重?我们从产品的角度看看:每个包含商品卡的页面,都需要一些数据来论证他的价值,那此时用户通过商品卡进入商品详情时,应该带来一个特殊标志,来表明来源
这种场景我们就有必要统一一个参数,比如 enter_form
,一般情况下 enter_form
会取自上一级页面的 open schema
(打开对应链接)的 query 参数上,在商品详情页初始化时,触发 page_view
事件,我们可以带上 query 上的 enter_form
参数(xxxxxxxx?enter_form=footprint),就可以精准地判断来源了
回归主题,这个例子说明了什么?
- 具体来看,我们给一个分析页面来源的方式统一了一个参数命名 与获取标准
- 抽象来看,我们给一个类型的事件统一了
SOP
(Standard Operating Procedure 标准作业程序)
有什么好处? 🌟🌟🌟
- 数据准确与一致性 :全域业务线统一标准,在任何同类场景中使用一种标准规范,保证数据不会出现差异性(比如一个页面用了
enter_form
作为来源,而另一个页面用了origin
) - 上下文一致性 :保证需求沟通过程中,避免理解 gap(比如我满嘴
enter_form
,另一边的规范却是origin
,就会导致沟通低效,甚至出现理解错误导致数据回收有误的情况)
当然,统一参数标准还有更多的体现方式,也有很多的推进思路(如设立一套涵盖:测试环节标准 SOP 指定、本地脚本检查等卡点的检查流程,保证埋点需求顺利上线)
背景问题二:复杂的归因链路
背景带入
何为复杂的归因链路?我们还是以电商的商品成单归因为例:我们需要分析出商品详情的来源 ,保证相关场景的价值论证能力
我们在前面提到了,可以通过 enter_form
来归因,但在这个场景远没有那么简单 !
我们可以设想一个用户行为:
- 用户通过抖音进入了抖音商城
- 用户在首页的商品瀑布流进行浏览,并点击了一个商品进行查看
- 一天后,用户想要再看看之前那个商品,所以来到了足迹页面
- 用户找到了商品,并点击进入商品详情,把商品加入了购物车
- 又一天后,用户发现购物车的这个商品打折了,赶紧进入该商品详情并火速完成了下单
好了,我们分析一下这个链路出现了几个商品详情来源:首页商品瀑布流、足迹、购物车
如果套用我们之前的方法,仅计算成单前商品详情的来源,那么只有购物车会被记入来源,那瀑布流和足迹肯定就不乐意了(我也参与了整个交易链路,为什么没有我的功劳?!)
这种问题会带来什么后果??(远比你想象的严重)
- 价值论证不置信 :每个场景的价值论证都取决于当前场景的属性,足迹这些场景起到的作用,不是一定要以成单前商品详情的来源为标准。相应的,我们会把整个链路中涉及商品卡的场景都记录在内,这样才能准确地论证某个场景的业务价值
- 分佣出现错误:基于上述的论证标准,如果我们忽视了前置链路,那可能就会存在分佣问题(比如某个达人推荐的商品,会因为中间链路不记录而导致分佣数据丢失),那必然会存在客诉(人家的利益受到了挑战)
解决方案
那我们该如何解决这个问题呢?是不是应该加一个数组到 schema 的 query 上?
1. 获取参数
然而并不是很合适!
schema 上的参数应该做内容的限制,如果堆上了一个长度不可控的数组,可能导致 schema 过长导致跳转失效、数据丢失等情况
那我们有什么办法呢?
有一个比较好的思路,就是给我们埋点的全局参数上,加一个数组,用来记录上级来源,每次页面跳转时,我们都会读取 query 上的 enter_form
,并更新这个全局参数
2. 参数内容规范
这样还有什么问题?我们再思考思考
- 每个页面都不一定有
enter_form
,且enter_form
的内容也可能会比较随意不可控 enter_form
如果仅仅表达footprint
、feeds
等英文名,其实非常的粗糙
那我们还有什么优化方案?>> 参数内容规范
首先,我们不再基于 enter_form
来获取来源数据。我们约定每个页面都有一个特殊标志位 (具备唯一性),在每个页面初始化埋点 sdk 的时候,都要注册一下这个标志位(可以理解为一种通参)
那么我们就通过另一个参数来上报,比如我们叫 origins
吧,那么上报的格式就是这样的:
js
page_view {
page_name: 'product_detail',
enter_form: 'footprint',
origin: 'x3',
origins: ['x1', 'x2', 'x3'],
// ......
}
这个标志位我们可以通过一个管理平台、或者规范文档来进行收敛,来保证唯一性和研发的标准性
其次,我们要思考如何让这个标志位更加的具备信息密度
我们可以通过点位信息的思想来满足这个诉求,比如:x1 = "业务线A.页面B.块C.点D_[更多信息......]" 我们通过一套规范的格式,来传递更多的信息,我们再通过几个场景来加深大家的理解:
page_view
,页面级曝光,x = "电商.商品详情"product_click
,足迹页面的商品卡点击,x = "电商.足迹.商品列表.商品卡_18" (18 表示第十八个商品卡)
那带入我们的整个归因链路,是不是就变成了:
js
page_view {
page_name: 'product_detail',
enter_form: 'footprint',
origin: '电商.商品详情',
origins: ['电商.商城首页.顶 tab 按钮集.足迹按钮', '电商.足迹.商品列表.商品卡_18', '电商.商品详情'],
// ......
}
到这一步,相比我们使用 enter_form
,是不是好了太多?
有了这样的数据,那么数据分析师就可以结合这些信息,做出更严格、精确的价值论证、收益分佣等操作~
最后
不知不觉已经写了很多内容,本来预期再聊一些个人对埋点研发生命周期的思考,考虑到篇幅,我们还是放到下一次再介绍~(我会快马加鞭更新的😭)
在真正的企业级埋点需求开发中,这些问题层出不穷,不规范、上下文不对齐等一系列问题都会带来非常恶心的研发体验(亲身经历~)
这种时候,借助 AI 也是一个不错的选择,本人最近就是在研究 Trae 对我埋点开发的提效手段,体验还是非常不错的。我们可以在 Chat 模式下投喂相关的产品 PRD,让他做一些工作:
- 基于 PRD 生成一个埋点设计表格
- 你在微调表格后,进入 Build 模式,让 AI 基于你的历史开发规范,注入相关的埋点事件(前提是你把一个个埋点需要注入的地方明确好!)
最后的最后,希望这篇文章能给大家带来一定的收获,咱们下次再见 👋