HarmonyOS A/B 实验治理实战:假设、分流、指标与停机规则
HarmonyOS A/B 实验治理实战:假设、分流、指标与停机规则
A/B 实验如果只做“给一半用户看新页面”,很容易变成线上赌博:不知道实验假设是什么,不知道核心指标是什么,不知道什么时候停止,也不知道异常时怎么回退。真正可用的实验系统,必须把假设、分流、指标、风险和决策写清楚。
本文从 HarmonyOS 应用侧整理一套 A/B 实验治理方法。示例代码用 ArkTS 风格表达,可以对接远程配置、Analytics Kit 或团队实验平台。

1. 实验开始前先写清假设
| 实验要素 | 示例 |
|---|---|
| 改动点 | 首页推荐卡片从列表改成横滑 |
| 目标指标 | 点击率提升 |
| 风险指标 | 崩溃率、首屏耗时 |
| 停机条件 | 崩溃率超阈值立即停止 |
没有假设的实验,很难做决策;没有停机条件的实验,风险不可控。

2. 实验资料边界和模块落点
| 资料或位置 | 作用 |
|---|---|
| 华为开发者文档中心:https://developer.huawei.com/consumer/cn/doc/ | 查询 AGC、分析、远程配置相关入口 |
| HarmonyOS 指南:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ | 查询应用配置、日志和数据能力 |
experiment/ExperimentPlan.ets |
定义实验假设和指标 |
experiment/BucketAssigner.ets |
稳定分流 |
experiment/MetricCollector.ets |
记录曝光和转化 |
experiment/DecisionBoard.ets |
判断继续、停止或全量 |
实验治理最好和远程配置、埋点事件共用基础设施。否则分流和数据很难对齐。
项目里建议按下面职责拆分:
| 项目位置 | 建议职责 |
|---|---|
experiment/ExperimentPlan.ets |
定义实验假设、分组和指标 |
experiment/BucketAssigner.ets |
保证同一用户稳定入桶 |
experiment/MetricCollector.ets |
记录曝光、转化和风险指标 |
experiment/ExperimentGuard.ets |
根据风险指标决定是否停机 |
experiment/DecisionBoard.ets |
输出继续、停止、全量或回退结论 |
这套拆分能避免实验逻辑散落在页面里。页面只关心自己属于哪个 variant,不决定实验是否成功。
3. ExperimentPlan 定义实验契约
interface ExperimentPlan {
experimentId: string;
hypothesis: string;
variants: string[];
primaryMetric: string;
guardMetrics: string[];
owner: string;
}
const homeCardExperiment: ExperimentPlan = {
experimentId: 'home_card_v2',
hypothesis: '横滑卡片能提升首页推荐点击率',
variants: ['A', 'B'],
primaryMetric: 'home_card_click_rate',
guardMetrics: ['crash_rate', 'startup_p95'],
owner: 'growth-team',
};
实验计划不是文档装饰,它决定后续分流、埋点和决策看板该看什么。
4. BucketAssigner 保证用户稳定分流
class BucketAssigner {
assign(experimentId: string, userId: string, variants: string[]): string {
const seed = `${experimentId}:${userId}`;
let hash = 0;
for (let i = 0; i < seed.length; i++) {
hash = (hash * 31 + seed.charCodeAt(i)) % 100000;
}
return variants[hash % variants.length];
}
}
同一个用户在同一个实验里必须稳定进入同一个桶。否则用户体验会跳变,指标也会被污染。
5. 指标采集要区分曝光和转化
interface ExperimentEvent {
experimentId: string;
variant: string;
eventName: 'exposure' | 'conversion' | 'guard';
metricName: string;
createdAt: number;
}
class MetricCollector {
private readonly events: ExperimentEvent[] = [];
record(event: ExperimentEvent): void {
this.events.push(event);
}
list(): ExperimentEvent[] {
return this.events.slice();
}
}
实验至少要记录曝光和转化。只记录点击,不记录曝光,无法计算真实转化率。

6. 风险指标决定是否停机
interface GuardMetricSnapshot {
metricName: string;
value: number;
stopValue: number;
}
class ExperimentGuard {
shouldStop(metrics: GuardMetricSnapshot[]): boolean {
return metrics.some((item) => item.value >= item.stopValue);
}
}
增长实验不能只看正向指标。崩溃率、错误率、耗时、投诉率都可能成为停机条件。
7. DecisionBoard 给出实验结论
type ExperimentDecision = 'continue' | 'stop' | 'shipA' | 'shipB';
class DecisionBoard {
decide(primaryLift: number, guardStopped: boolean): ExperimentDecision {
if (guardStopped) {
return 'stop';
}
if (primaryLift > 0.05) {
return 'shipB';
}
if (primaryLift < -0.02) {
return 'shipA';
}
return 'continue';
}
}
决策规则要在实验前写好,避免实验结束后按结果倒推解释。
8. 实验上线前验证动作
| 场景 | 操作 | 预期结果 |
|---|---|---|
| 同一用户重复进入 | 多次 assign | variant 不变 |
| 不同实验 | 换 experimentId | 可进入不同桶 |
| 只曝光不点击 | 记录 exposure | 漏斗可计算 |
| 风险超阈值 | crash_rate 超线 | decision 为 stop |
| 指标未显著 | lift 接近 0 | 继续观察 |
实验验收要做两类用户:同一个用户多次进入,确认分组稳定;不同用户进入,确认分组比例大体符合预期。还要检查埋点是否携带 experimentId 和 variant,否则后续指标无法归因。
interface ExperimentReleaseCheck {
planApproved: boolean;
bucketStable: boolean;
exposureTracked: boolean;
guardMetricReady: boolean;
stopRuleTested: boolean;
}
const abReleaseCheck: ExperimentReleaseCheck = {
planApproved: true,
bucketStable: true,
exposureTracked: true,
guardMetricReady: true,
stopRuleTested: true,
};
这份记录要在实验开始前完成。实验开始后再补指标,很容易出现“数据不够用但已经影响用户”的问题。
9. 实验异常排查表
| 现象 | 优先检查 | 修复方式 |
|---|---|---|
| 用户界面跳变 | 分流是否稳定 | 基于 userId 和 experimentId 哈希 |
| 转化率算不出 | 是否缺曝光 | 补 exposure 事件 |
| 指标提升但投诉变多 | guardMetrics 是否缺失 | 加风险指标 |
| 实验无法结束 | 是否缺决策规则 | 提前定义 stop/ship 条件 |
| 数据和配置不一致 | 桶信息是否上报 | 埋点带 variant |
排查实验异常时先看分流,再看曝光,再看转化。分流不稳定会污染所有数据;曝光缺失会导致转化率失真;风险指标缺失会让实验无法及时停机。
实验结束后还要保存一份结论记录,避免几个月后只记得“当时好像 B 方案更好”。结论记录至少包含实验 ID、样本范围、主指标变化、风险指标、最终决策和上线版本。
interface ExperimentConclusion {
experimentId: string;
winner: 'A' | 'B' | 'none';
primaryLift: number;
guardPassed: boolean;
decision: ExperimentDecision;
shippedVersion?: string;
}
const homeCardConclusion: ExperimentConclusion = {
experimentId: 'home_card_v2',
winner: 'B',
primaryLift: 0.061,
guardPassed: true,
decision: 'shipB',
shippedVersion: '1.5.0',
};
这份记录能帮助后续版本复盘。如果同类页面再次改版,可以直接查历史实验,而不是重新争论一次。
实验专项证据包:停机规则要比结论更早写
A/B 实验最容易犯的错是先上线再想指标。可靠实验要先写假设、分流规则、主指标、护栏指标和停机规则。这样结果异常时不会靠主观判断。
| 字段 | 作用 |
|---|---|
hypothesis |
实验假设 |
bucket |
用户分组 |
guardMetric |
护栏指标 |
stopRule |
停机条件 |
interface ExperimentEvidence {
experimentId: string
hypothesis: string
bucket: 'A' | 'B'
guardMetric: string
stopRule: string
}
function assertExperimentEvidence(e: ExperimentEvidence): void {
if (e.hypothesis.length < 10) throw new Error('实验假设不够明确')
if (!e.guardMetric || !e.stopRule) throw new Error('实验缺少护栏或停机规则')
}
这段代码把实验从“开关对比”提升为有边界的决策系统。
实验停机复现场景:给读者一组可执行核验
A/B 实验不能只写分流,还要说明什么时候停止。转化率提升但崩溃上涨时,护栏指标应优先于业务指标。
| 核验维度 | 读者需要准备的证据 |
|---|---|
| 输入 | 页面入口、用户动作、关键参数 |
| 过程 | 日志、状态变化、异常分支 |
| 输出 | UI 表现、回调结果、持久化结果 |
| 回归 | 同场景重复执行后的结果 |
interface ExperimentReplayCase {
experimentId: any
mainMetric: any
guardMetric: any
stopReason: any
}
const replay71: ExperimentReplayCase = {
experimentId: 'sample',
mainMetric: 'sample',
guardMetric: 'sample',
stopReason: 'sample',
}
function assertReplay71(item: ExperimentReplayCase): void {
if (item.guardMetric.length === 0) throw new Error('实验缺少护栏指标')
}
这组核验把实验假设和停机规则前置,避免实验上线后只看单一转化指标做判断。
实验误判回放表:把文章方法变成可复现动作
实验文章要展示误判防线。可以构造一个转化率提升但崩溃上涨的场景,证明护栏指标会阻止继续放量,而不是只看主指标得出结论。
| 回放动作 | 核验方式 |
|---|---|
| 主指标上涨 | 准备输入、执行操作、记录结果、给出结论 |
| 护栏指标异常 | 准备输入、执行操作、记录结果、给出结论 |
| 停机规则触发 | 准备输入、执行操作、记录结果、给出结论 |
| 实验状态冻结 | 准备输入、执行操作、记录结果、给出结论 |
实验回放要把主指标和护栏指标一起看。读者可以在实验启动前写清楚:如果主指标提升但崩溃、投诉、耗时或退款任一护栏异常,就暂停继续放量。这个规则写在实验开始前,比结果出来后再争论更可靠。文章里的实验方案也应强调决策边界,而不是只展示分流代码。
实验决策的落地边界:不要把边界留给读者猜
实验不是让所有功能都用数据投票。适合实验的是有明确假设、可量化指标、可控制风险的功能;不适合实验的是安全、隐私、支付一致性和强合规能力。读者落地时要先判断功能能不能实验,再决定分流和指标。
| 落地项 | 处理要求 |
|---|---|
| 安全能力不做随机实验 | 需要有明确输入、处理边界和失败兜底 |
| 支付链路不牺牲一致性 | 需要有明确输入、处理边界和失败兜底 |
| 体验功能可小流量验证 | 需要有明确输入、处理边界和失败兜底 |
| 运营策略需设置停机线 | 需要有明确输入、处理边界和失败兜底 |
这类边界写清楚后,读者不需要猜哪些逻辑属于页面、哪些属于服务、哪些属于发布前验收。文章的价值也会从“讲了一个功能”变成“给了一套可迁移的工程判断”。
实验联调步骤:按真实路径走一遍
实验上线前建议先用本地固定分组跑通页面,再用灰度用户跑真实分流。第一步确认用户稳定进入 A 或 B,第二步确认同一用户不会频繁换组,第三步确认主指标和护栏指标同时记录,第四步确认停机后新用户不再进入实验,第五步确认历史用户状态有明确处理规则。这样实验不会变成一个不可解释的开关。
这一步的意义是让读者拿到文章后可以直接复现,而不是只理解概念。技术文章如果能把“输入、动作、日志、结果、失败兜底”写完整,读者照着做时出错概率会低很多。
10. 小结:实验是决策系统,不是换皮开关
HarmonyOS 应用做 A/B 实验,要从假设开始,到稳定分流、指标采集、风险停机和最终决策结束。实验系统如果没有这些边界,只会制造更多不确定性;如果这些边界清楚,它就能帮助团队用数据做可追溯决策。
更多推荐



所有评论(0)