跳到主要内容

设计 A/B 测试框架

问题

如何设计 iOS 端的 A/B 测试框架?

答案

架构

核心实现

class ABTestManager {
static let shared = ABTestManager()
private var experiments: [String: Experiment] = [:]

// 拉取实验配置
func fetchConfig() async {
let config = try? await API.fetchABConfig()
config?.experiments.forEach { exp in
experiments[exp.key] = exp
}
// 缓存到本地
UserDefaults.standard.set(try? JSONEncoder().encode(config), forKey: "ab_config")
}

// 获取分组
func getVariant(experimentKey: String) -> String {
guard let experiment = experiments[experimentKey] else { return "control" }

// hash 分流
let userId = UserManager.shared.userId ?? UUID().uuidString
let hashInput = "\(experimentKey)_\(userId)"
let hashValue = abs(hashInput.hashValue) % 100

var cumulative = 0
for variant in experiment.variants {
cumulative += variant.weight
if hashValue < cumulative {
// 曝光上报
Analytics.track("ab_exposure", params: [
"experiment": experimentKey,
"variant": variant.name
])
return variant.name
}
}
return "control"
}
}

// 使用
if ABTestManager.shared.getVariant(experimentKey: "new_checkout") == "treatment" {
showNewCheckoutFlow()
} else {
showOldCheckoutFlow()
}

关键设计点

设计点方案
分流一致性同一用户 + 同一实验 = 同一分组
分层实验不同层独立分流,互不影响
缓存首次启动用上次配置,后台更新
杀掉实验服务端关闭实验 = 全部走 control

常见面试问题

Q1: 如何保证 A/B 分组的稳定性?

答案:使用 hash(userId + experimentKey) % 100 确定分组,同一用户每次计算结果一致。避免用随机数,否则每次启动可能分到不同组。

相关链接