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 继续观察

实验验收要做两类用户:同一个用户多次进入,确认分组稳定;不同用户进入,确认分组比例大体符合预期。还要检查埋点是否携带 experimentIdvariant,否则后续指标无法归因。

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 实验,要从假设开始,到稳定分流、指标采集、风险停机和最终决策结束。实验系统如果没有这些边界,只会制造更多不确定性;如果这些边界清楚,它就能帮助团队用数据做可追溯决策。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐