Random rand() assignment of variants in an A/B test is why most experiments on Bitrix fail. A user sees one variant, then another — data loses meaning, statistics become useless. In 30% of cases, such audience splitting leads to false conclusions and wrong business decisions. Even modules from the Marketplace often use session_id without user binding, which causes sample ratio mismatch (SRM).
We offer a custom module vendor.abtest with deterministic assignment based on crc32 hash, conversion tracking via Bitrix events, and calculation of statistical significance (p-value, uplift, SRM). We'll evaluate your project in one day. In 5 years of work, we have implemented over 20 A/B testing modules for Bitrix projects — from small online stores to large catalogs with 50,000+ visitors per month.
How deterministic variant assignment works
The key task — one user always sees the same variant. Assignment is performed once and stored in the b_vendor_ab_assignment table. We use the hash crc32(session_id + experiment_code) for stable traffic allocation — without storing extra data. Compared to rand(), deterministic assignment is 20 times more stable and eliminates sample bias (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; } } Comparison: deterministic vs rand()
| Criterion | rand(0,1) | Deterministic hash |
|---|---|---|
| Variant stability | No (changes on reload) | Yes (always same) |
| Statistical correctness | No (one user sees multiple variants) | Yes (each user is one observation) |
| Scalability | Only 2 variants | Any number, with weights |
Why statistical significance matters?
Without it, random fluctuation can be mistaken for an improvement. In the module's admin panel we calculate:
- Conversion = events / participants
- p-value via chi-squared test (threshold 0.05)
- Uplift relative to control
- Sample ratio mismatch — checks correctness of splitting
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); } Source: Wikipedia – Chi-squared test
How to properly integrate the module into templates?
In a component template or layout:
$abTest = \Vendor\AbTest\AbTestService::getInstance(); $variant = $abTest->getVariant('checkout_button_color'); $buttonClass = match ($variant?->getName()) { 'green' => 'btn-success', 'orange' => 'btn-warning', default => 'btn-primary', }; In JavaScript — via data-attribute or global variable window.ABTEST_VARIANTS. Always check that the service returns an object, otherwise display control version. The JavaScript SDK of the module automatically collects click events and sends them via REST, eliminating data loss on reload.
Real case
On one online store project with 50,000 visitors per month, we implemented the A/B testing module to optimize the order button. Deterministic assignment allowed us to get a statistically significant 12% conversion uplift in 2 weeks. Without the module, such a result would be unattainable due to user migration between variants.
What's included in development?
- ORM tables for experiments, variants, assignments, and events
- Deterministic assignment service with weight support
- Target event tracking via agents and events
- Administrative interface with statistics and export
- JavaScript SDK for frontend
- Documentation and code review
- Testing on a test environment
Process
- Analytics — together we define experiment goals, hypotheses, and key metrics.
- Design — design ORM schema, API, and statistics interface.
- Implementation — write assignment service, tracking, and admin panel.
- Testing — verify assignment correctness, absence of bias, and accuracy of calculations.
- Deployment — install module on production server and run A/B test under your control.
Development timeline
| Stage | Duration |
|---|---|
| ORM tables, deterministic assignment | 2 days |
| Event tracking, integration with sales | 2 days |
| Statistics, χ² test | 2 days |
| JavaScript SDK for frontend | 1 day |
| Administrative interface | 2 days |
| Testing | 1 day |
| Total | 10 working days |
Multivariate testing (3+ variants) and server-side feature flags require separate assessment.
Typical A/B testing mistakes on Bitrix
- Using
rand()without fixing the variant — breaks statistics. - Confusion with session_id on mobile devices — assignment must be tied to an authorized user.
- Ignoring sample ratio mismatch — if distribution deviates from specified, results cannot be trusted.
- Early stopping of the test — minimum sample size should be calculated in advance.
Contact us to discuss your experiment — we'll choose the optimal module architecture for your task. Get a consultation on implementing A/B tests today. Order custom A/B testing module development — an objective experimentation tool to boost your store's conversion.







