默认值与回退
ABetterChoice SDK 的设计原则是:永不阻塞应用热路径。当平台不可达时,SDK 会按下面三层依次 回退:
- 上一次成功轮询写入的内存缓存;
- 客户端 SDK 在磁盘上保留的预计算包;
- 你在读取参数 / 开关时传入的默认值。
本页描述每一层的行为,帮助你挑出安全的默认值,并清楚平台抖动时代码会发生什么。
下文 Go 与 HTTP 部分代表今天已正式可用的接入方式。Android、iOS、JavaScript、C++ 部分是 形态预览,这些 SDK 在
Settings → SDK&Key下都是Coming soon。
读取 API 的形态
每个 SDK 都暴露带类型的读取 API,并接受默认值。当参数 / 开关缺失、网络断开、实验已归档时, 返回的就是这个默认值。
go
// Go
show := exp.GetBoolWithDefault("should_show_banner", false)
size := exp.GetInt64WithDefault("size", 16)cpp
// C++
auto exp = AbcService::GetInstance()->GetExperiment("6666", "abc_layer_name", WithSetUnitID(uid));
std::string color = exp.GetString("color"); // 缺失时返回空串java
// Android
AbcExpInfo info = mAbcExperiment.getExperiment("abc_layer_name");
boolean show = info.getBooleanValue("should_show_banner", true);
String copy = info.getStringValue("copy", "Default copy");javascript
// JavaScript
const exp = abc.getExperiment("abc_layer_name", true);
const show = exp.getBoolValue("should_show_banner", true);默认值是契约的一等成员:哪怕 unit_id 永远没被分到任何实验,这个值也要让系统继续保持 正确。
回退链路
实践中,你只会在以下场景看到默认值:
- 初始化尚未完成的第一次调用(最好在
init完成后再读); - 网络故障时长超过 SDK 容忍窗;
- 该参数 / 开关从未返回过值(拼写错误、已归档、已删除)。
服务端 SDK 行为
服务端 SDK 每 3 秒轮询并把配置缓存在内存。一次轮询失败时,SDK 继续用上一次的缓存。没有磁盘 持久化 —— 进程重启时,初始化必须先成功,读取才能拿到真实值。
实操建议:
- 让初始化以有限超时阻塞(默认 15 秒),避免空缓存就开始服务流量;
- 为 "init succeeded" / "fell back to defaults" 各打一个指标,避免静默退化;
- 默认值不要比对照组更激进 —— 平台静默时用户看到的就是它。
客户端 SDK 行为
客户端 SDK 把上一次成功的包持久化到本地(Android / iOS 用 MMKV,Web 用 localStorage)。下次 启动时:
- 立刻返回持久化值,避免 UI 闪现默认值;
- 后台请求刷新缓存;
- 若刷新失败,读取继续返回持久化值;只有当本地文件缺失或损坏时,才落到调用方默认值。
实操建议:
- UI 切换以参数为准,不要依赖"网络是否就绪"信号 —— SDK 已处理重试;
- 默认值要匹配对照体验,让首次启动用户看到与回访用户一致的 UI;
- 长 Web 会话上保持
enableAutomaticPoll: true,会话期内推送的更新也能在 10 分钟内生效。
HTTP API 行为
HTTP API 没有客户端缓存。每次调用都是新请求:
- 传输失败时调用返回错误,由客户端选用默认值;
ret_code == 100但exp_data中缺该层时,说明该unit_id在这一层未分到任何实验, 使用默认值;ret_code != 100时按调用失败处理并使用默认值。完整含义见 HTTP API 端点。
选一个安全的默认值
照下面三条做就不会后悔:
- 默认值始终选 control 分支,不要选 treatment。如果 treatment 会破坏用户流,默认值要 保证流不破。
- 使用静态默认值,不用"最常见的分组"。静态值可复现、可测试;动态值在故障时会自己漂移。
- 类型严格匹配。对
Boolean参数传字符串默认值是未来的 bug —— 一直用上面的强类型 API 就 能避免。