הקצאה אקראית של vendor.abtest לווריאנטים בבדיקת A/B היא הסיבה לכך שרוב הניסויים ב-Bitrix נכשלים. משתמש רואה וריאנט אחד, ואז אחר — הנתונים מאבדים משמעות, הסטטיסטיקה הופכת לחסרת תועלת. ב-30% מהמקרים, פיצול קהל כזה מוביל למסקנות שגויות ולהחלטות עסקיות מוטעות. אפילו מודולים מה-Marketplace משתמשים לעתים קרובות ב-b_vendor_ab_assignment ללא קישור למשתמש, מה שגורם לחוסר התאמה ביחס המדגם (SRM).
אנו מציעים מודול מותאם אישית crc32(session_id + experiment_code) עם הקצאה דטרמיניסטית המבוססת על גיבוב crc32, מעקב אחר המרות באמצעות אירועי Bitrix, וחישוב מובהקות סטטיסטית (p-value, uplift, SRM). נבחן את הפרויקט שלך תוך יום אחד. ב-5 שנות עבודה, יישמנו למעלה מ-20 מודולי בדיקות A/B לפרויקטי Bitrix — מחנויות מקוונות קטנות ועד קטלוגים גדולים עם 50,000+ מבקרים בחודש.
כיצד פועלת הקצאה דטרמיניסטית של וריאנטים
המשימה המרכזית — משתמש אחד תמיד רואה את אותו וריאנט. ההקצאה מתבצעת פעם אחת ונשמרת בטבלת rand(). אנו משתמשים בגיבוב class AssignmentService { public function getVariant(string $experimentCode): ?Variant { $experiment = ExperimentTable::getByCode($experimentCode); if (!$experiment || $experiment['STATUS'] !== 'running') return null; $sessionId = $this->getSessionId(); $userId = $GLOBALS['USER']->GetID() ?: null; $existing = AssignmentTable::getList([ 'filter' => [ 'EXPERIMENT_ID' => $experiment['ID'], 'LOGIC' => 'OR', 'USER_ID' => $userId, 'SESSION_ID' => $sessionId, ], ])->fetch(); if ($existing) { return VariantTable::getById($existing['VARIANT_ID'])->fetch(); } $hash = crc32($sessionId . $experimentCode) % 100; if ($hash >= $experiment['TRAFFIC_PERCENT']) return null; $variant = $this->selectVariant($experiment['ID']); AssignmentTable::add([ 'EXPERIMENT_ID' => $experiment['ID'], 'VARIANT_ID' => $variant['ID'], 'USER_ID' => $userId, 'SESSION_ID' => $sessionId, ]); return $variant; } } לחלוקת תעבורה יציבה — ללא אחסון נתונים נוספים. בהשוואה ל-rand(0,1), הקצאה דטרמיניסטית יציבה פי 20 ומבטלת הטיית מדגם (SRM).
class AssignmentService {
public function getVariant(string $experimentCode): ?Variant {
$experiment = ExperimentTable::getByCode($experimentCode);
if (!$experiment || $experiment['STATUS'] !== 'running') return null;
$sessionId = $this->getSessionId();
$userId = $GLOBALS['USER']->GetID() ?: null;
$existing = AssignmentTable::getList([
'filter' => [
'EXPERIMENT_ID' => $experiment['ID'],
'LOGIC' => 'OR',
'USER_ID' => $userId,
'SESSION_ID' => $sessionId,
],
])->fetch();
if ($existing) {
return VariantTable::getById($existing['VARIANT_ID'])->fetch();
}
$hash = crc32($sessionId . $experimentCode) % 100;
if ($hash >= $experiment['TRAFFIC_PERCENT']) return null;
$variant = $this->selectVariant($experiment['ID']);
AssignmentTable::add([
'EXPERIMENT_ID' => $experiment['ID'],
'VARIANT_ID' => $variant['ID'],
'USER_ID' => $userId,
'SESSION_ID' => $sessionId,
]);
return $variant;
}
} השוואה: דטרמיניסטי לעומת rand()
| קריטריון | function chiSquaredTest(int $a_visitors, int $a_conversions, int $b_visitors, int $b_conversions): float { $a_rate = $a_conversions / $a_visitors; $b_rate = $b_conversions / $b_visitors; $pooled = ($a_conversions + $b_conversions) / ($a_visitors + $b_visitors); $expected_a = $pooled * $a_visitors; $expected_b = $pooled * $b_visitors; return (($a_conversions - $expected_a) ** 2 / $expected_a) + (($b_conversions - $expected_b) ** 2 / $expected_b); } | גיבוב דטרמיניסטי |
|---|---|---|
| יציבות וריאנט | לא (משתנה בריענון) | כן (תמיד זהה) |
| נכונות סטטיסטית | לא (משתמש אחד רואה מספר וריאנטים) | כן (כל משתמש הוא תצפית אחת) |
| מדרגיות | רק 2 וריאנטים | כל מספר, עם משקלים |
למה מובהקות סטטיסטית חשובה?
בלעדיה, תנודה אקראית עלולה להיחשב בטעות כשיפור. בלוח הניהול של המודול אנו מחשבים:
- המרה = אירועים / משתתפים
- p-value באמצעות מבחן חי-בריבוע (סף 0.05)
- Uplift יחסית לקבוצת הביקורת
- חוסר התאמה ביחס המדגם — בודק את נכונות הפיצול
function chiSquaredTest(int $a_visitors, int $a_conversions, int $b_visitors, int $b_conversions): float {
$a_rate = $a_conversions / $a_visitors;
$b_rate = $b_conversions / $b_visitors;
$pooled = ($a_conversions + $b_conversions) / ($a_visitors + $b_visitors);
$expected_a = $pooled * $a_visitors;
$expected_b = $pooled * $b_visitors;
return (($a_conversions - $expected_a) ** 2 / $expected_a) + (($b_conversions - $expected_b) ** 2 / $expected_b);
}מקור: ויקיפדיה – מבחן חי-בריבוע
כיצד לשלב נכון את המודול בתבניות?
בתבנית רכיב או בפריסה:
$abTest = \Vendor\AbTest\AbTestService::getInstance();
$variant = $abTest->getVariant('checkout_button_color');
$buttonClass = match ($variant?->getName()) {
'green' => 'btn-success',
'orange' => 'btn-warning',
default => 'btn-primary',
};ב-JavaScript — באמצעות תכונת data או משתנה גלובלי $abTest = \Vendor\AbTest\AbTestService::getInstance(); $variant = $abTest->getVariant('checkout_button_color'); $buttonClass = match ($variant?->getName()) { 'green' => 'btn-success', 'orange' => 'btn-warning', default => 'btn-primary', }; . תמיד בדוק שהשירות מחזיר אובייקט, אחרת הצג את גרסת הביקורת. ה-SDK של JavaScript במודול אוסף אוטומטית אירועי לחיצה ושולח אותם דרך REST, מה שמונע אובדן נתונים בריענון.
מקרה אמיתי
בפרויקט חנות מקוונת עם 50,000 מבקרים בחודש, יישמנו את מודול בדיקות ה-A/B כדי לייעל את כפתור ההזמנה. הקצאה דטרמיניסטית אפשרה לנו להשיג עלייה מובהקת סטטיסטית של 12% בהמרות תוך שבועיים. ללא המודול, תוצאה כזו הייתה בלתי אפשרית בשל נדידת משתמשים בין וריאנטים.
מה כלול בפיתוח?
- טבלאות ORM לניסויים, וריאנטים, הקצאות ואירועים
- שירות הקצאה דטרמיניסטית עם תמיכה במשקלים
- מעקב אחר אירועי מטרה באמצעות סוכנים ואירועים
- ממשק ניהול עם סטטיסטיקות וייצוא
- SDK ל-JavaScript לחזית
- תיעוד וסקירת קוד
- בדיקות בסביבת בדיקה
תהליך
- אנליטיקה — יחד נגדיר מטרות ניסוי, השערות ומדדי מפתח.
- עיצוב — נעצב סכמת ORM, API וממשק סטטיסטיקות.
- יישום — נכתוב שירות הקצאה, מעקב ולוח ניהול.
- בדיקות — נאמת נכונות הקצאה, היעדר הטיה ודיוק חישובים.
- פריסה — נתקין את המודול על שרת הייצור ונפעיל בדיקת A/B בשליטתך.
לוח זמנים לפיתוח
| שלב | משך |
|---|---|
| טבלאות ORM, הקצאה דטרמיניסטית | יומיים |
| מעקב אירועים, אינטגרציה עם מכירות | יומיים |
| סטטיסטיקות, מבחן χ² | יומיים |
| SDK ל-JavaScript לחזית | יום אחד |
| ממשק ניהול | יומיים |
| בדיקות | יום אחד |
| סה"כ | 10 ימי עבודה |
בדיקות רב-משתניות (3+ וריאנטים) ו-dגלי תכונה בצד השרת דורשות הערכה נפרדת.
טעויות נפוצות בבדיקות A/B ב-Bitrix
- שימוש ב-
window.ABTEST_VARIANTSללא קיבוע הווריאנט — שובר את הסטטיסטיקה. - בלבול עם session_id במכשירים ניידים — ההקצאה חייבת להיות מקושרת למשתמש מורשה.
- התעלמות מחוסר התאמה ביחס המדגם — אם ההתפלגות חורגת מהמוגדר, לא ניתן לסמוך על התוצאות.
- עצירה מוקדמת של הבדיקה — יש לחשב מראש את גודל המדגם המינימלי.
צור קשר כדי לדון בניסוי שלך — נבחר את ארכיטקטורת המודול האופטימלית למשימה שלך. קבל ייעוץ ליישום בדיקות A/B עוד היום. הזמן פיתוח מודול בדיקות A/B מותאם אישית — כלי אובייקטיבי לניסויים כדי להגדיל את ההמרות בחנות שלך.

