后端的简单A/B测试

tracks的Python项目详细描述


你有没有想过a/b测试后端的多个代码路径?考虑 以下是:管理层希望您降低服务器成本。你来吧 事实上,你真的不需要一个128 GB内存的32核野兽 你的椰子店-但是你能卖多低?让我们测试一下 椰子目录页影响转换!(首先,没有tracks

response=render_coconut_catalog()test_value=random.random()iftest_value<(1/3):# control groupsleep_sec=0eliftest_value<(2/3):sleep_sec=0.5else:sleep_sec=2time.sleep(sleep_sec)cur.execute('INSERT INTO test_runs (user_id, variant) VALUES (%s, %s)',user_id,sleep_sec)returnresponse

现在,这有点管用,但已经有点乱了,很容易就能摆脱 如果您现在考虑,请举手:

  • The thing you test is really, really simple
  • You don’t lock a user’s version to make sure they always get the same test variant, making your data more mushy with each page load.
  • You don’t exclude your most important users from testing to avoid hurting conversion among coconut addicts in your initial test run. (Of course you will need to include them later to make an informed decision.)
  • You already have a few more ideas about things you want to A/B test. Maybe a hundred.
  • You can add another 5 engineers to the project and they will probably each implement these tests slightly differently, and that’s no good.

输入tracks。首先,我们定义我们的变体:

classDelayTracks(tracks.SimpleTrackSet):@staticmethoddefshort_track():time.sleep(0.5)@staticmethoddeflong_track():time.sleep(2)

使用它:

response=render_coconut_catalog()withDelayTracks()astrack:track()cur.execute('INSERT INTO test_runs (user_id, variant) VALUES (%s, %s)',user_id,track.name)returnresponse

已经好一点了,但是如果我们看一下 上面的问题一个接一个。首先,我们可以轻松地使测试更加复杂:

classPricingTracks(tracks.SimpleTrackSet):@staticmethoddefexpensive_track(response):forcoconutinresponse['coconuts']:coconut['price']+=1@staticmethoddefcheap_track(response):forcoconutinresponse['coconuts']:coconut['price']-=1

没变得那么糟,是吗?让我们更进一步 变型:

classPricingTracks(tracks.ParamTrackSet):params=[{'price_delta':n}forninrange(-5,6)]add_control_track=False# {'price_delta': 0} is our control group@staticmethoddeftrack_name(price_delta):return'price_adjusted_by_{0}'.format(price_delta)@staticmethoddeftrack(response,price_delta):forcoconutinresponse['coconuts']:coconut['price']+=price_delta

大量的测试!让我们转到第二个要点。如何锁定 为用户服务的变体?只需更改用法,将用户密钥传递给曲目即可:

response=render_coconut_catalog()withDelayTracks(key=user_id)astrack:track()cur.execute('INSERT INTO test_runs (user_id, variant) VALUES (%s, %s)',user_id,track.name)returnresponse

密钥将被序列化为字符串,并将派生要使用的变量 从那根绳子上。当然,关键是什么;在大多数情况下,关键可能是 用户ID,但您可以使用用户的国家和 例如,项目id。(不知道为什么你会想要这个具体的例子, 但你明白了。)

所以,解决了这个问题,列出第3项,我们来了!如果我们担心 我们的顶级客户对我们在他们身上测试东西很生气?轻松点。

classDelayTracks(tracks.SimpleTrackSet):@propertydefis_eligible(self):returnnotself.context['user']['is_vip']# code of variants trimmedresponse=render_coconut_catalog()withDelayTracks(context={'user':user_dict})astrack:track()# will always be control for VIPs (even with `add_control_group = False`)iftrack.is_eligible:cur.execute('INSERT INTO test_runs (user_id, variant) VALUES (%s, %s)',(user_id,track.name))returnresponse

最后,让我们尝试同时运行延迟和定价测试!

tracksets=[DelayTracks,PricingTracks]response=render_coconut_catalog()withtracks.MultiTrackSet(tracksets,key=user_id)asmultitrack:multitrack()cur.execute('INSERT INTO test_runs (user_id, test, variant) VALUES (%s, %s)',(user_id,multitrack.trackset.name,multitrack.track.name),)returnresponse

因此,有了这个,tracks将收集用户符合条件的所有测试,并且 根据给定的密钥选择其中一个。我们也可以设置 DelayTracks.weight到1000,使其使用的可能性增加10倍 作为定价(默认权重为100.)

我们只有几行代码,不是吗?

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何从ImageIO中排除特定的TIFF读取器?   JavaJMockit和passbyreference。我们中一定有一个人错了(可能是我!)   java Android camera2放弃了牛轧糖的表面,但在棉花糖上工作   java按字符串中出现的顺序对字符数组进行排序   如何获取Groovy生成的java源代码   java无法使用AutoIT和Selenium Webdriver在所需位置/文件夹保存图像   java为什么我的冒泡排序代码会打印出这些奇怪的东西?   java JAXB:typesafeEnumMemberName=“generateName”是否可自定义?   Java编程输入:今天是星期天输出:星期天是今天   java不理解首个OOAD书的吉他示例   java如何从JformattedTextfield检索货币格式值   java可以从相同的源代码生成功能不同的可执行文件吗?