Development and Configuration of 1C-Bitrix Modules
The main trap in Bitrix is init.php. You put an OnBeforeIBlockElementUpdate handler there, then another one, and a year later the file is 2000 lines long, all executing on every hit. We move business logic into proper modules with D7 ORM, custom tables, and an administrative interface. A module can be disabled, transferred to another project, covered with tests — none of that is possible with init.php.
Standard Modules: Where the Pitfalls Are
Information blocks. IB architecture is the first thing we review on any project. Classic mistake: a single catalog infoblock with 80 properties, 30 of which are multiple-value. The b_iblock_element_property table bloats to millions of rows, and CIBlockElement::GetList filtering on three properties goes into a full table scan. We move reference data to Highload blocks, eliminate multiple-value properties where possible, and design the structure with the expectation that the catalog will grow 5x.
Online store (sale). A powerful module, but cart business rules are a story of their own. We configure discount priorities so two promotions don't stack to 60% instead of 30%, connect payment handlers, and set up OnSaleOrderBeforeSaved handlers for custom validation.
Search. The built-in search module with morphology works up to 10-15 thousand items. Beyond that — Elasticsearch. We configure it through the Bitrix search module API and index via CSearchFullText or custom indexers.
Highload blocks. For reference data, logs, user data — instead of bloated IBs. Direct queries via Bitrix\Highloadblock\HighloadBlockTable, custom tables instead of the EAV structure of standard infoblock. A million records — without degradation.
Mail events. Configuration isn't just about templates in b_event_message. The key is SPF, DKIM, DMARC on DNS — otherwise transactional emails land in spam. We verify deliverability and set up bounce handling.
Custom Module Development
Each module follows the structure /local/modules/vendor.modulename/:
-
install/index.php— installation class, table creation via$DB->RunSQLBatch() -
lib/— D7 ORM classes, inheriting fromBitrix\Main\ORM\Data\DataManager -
admin/— admin pages viaCAdminList,CAdminForm -
include.php— autoloading, handler registration viaEventManager::getInstance()->registerEventHandler() - REST API endpoints via
\Bitrix\Rest\RestManager
The module registers in the system, appears in the "Installed Solutions" list, and has its own settings at /bitrix/admin/settings.php?mid=vendor.modulename. It can be enabled, disabled, and updated via UpdateSystem or a custom migration mechanism.
What we've built:
- Promotions management — a visual condition builder via
CAdminCalendar, timers via agents (CAgent::AddAgent), effectiveness analytics linked to the sale module - Cost calculator — React widget on the frontend, REST API in the module, formulas stored in a Highload block
- Booking system — real-time calendar, locking via
$DB->StartTransaction()/$DB->Commit()for concurrent requests, synchronization with channel managers via webhook
Components and Composite Cache
Component customization — via result_modifier.php and component_epilog.php, not by editing the standard template's template.php. This way the core updates painlessly.
Composite cache ("Composite Site" technology) — the server delivers ready-made HTML, bypassing PHP routing. Dynamic zones (cart, authentication) are loaded via CBitrixComponent::setFrameMode(true) and AJAX. TTFB drops to 30-50 ms. But there are nuances: not all components are compatible, $APPLICATION->ShowPanel() breaks the composite, and careful <div id="bx-composite-..."> markup is required.
Marketplace: Audit Before Installation
Before installing a marketplace module — a mandatory audit. We check for: SQL queries without prepared statements (hello, SQL injection), direct access to $_REQUEST without filtering, use of the deprecated old core API instead of D7, conflicts with the composite cache module. A module with no updates for over a year and a couple dozen installations is most likely a headache at the next PHP update.
Migration to D7
When upgrading PHP to 8.x or transitioning to a new edition — refactoring of deprecated calls:
-
CIBlockElement::GetList()→Bitrix\Iblock\Elements\ElementTable::getList() -
CSaleOrder::GetList()→Bitrix\Sale\Order::getList() -
CModule::IncludeModule()→Bitrix\Main\Loader::includeModule() - Testing on staging, rollback via git if issues arise
Module Development Costs
| Complexity | Examples | Timeline | Approximate Cost |
|---|---|---|---|
| Simple | Callback widget, banner system, simple calculator | 3-5 days | from 30,000 RUB |
| Medium | Booking system, product configurator, review module with moderation | 1-2 weeks | from 80,000 RUB |
| Complex | Multi-regionality, custom loyalty program, ERP integration | 2-4 weeks | from 150,000 RUB |
| Enterprise | Marketplace platform, complex business processes with multiple roles | 1-3 months | from 350,000 RUB |
The cost includes design, development, testing, documentation, and deployment.
Module Testing
Unit tests via PHPUnit. We cover business logic: discount calculations, validation, document generation. Mocks for Bitrix\Main\Application::getConnection() — so tests don't depend on the database.
Integration tests. We verify event handlers on a real database — OnAfterIBlockElementAdd, OnSaleOrderSaved, and others. REST API endpoints are tested via curl or PHPUnit HTTP client. Critical for modules working with b_sale_order, b_catalog_price — where errors cost money.
Compatibility. PHP 7.4, 8.0, 8.1, 8.2. Editions: Standard, Small Business, Business. We check for conflicts with popular marketplace modules — they love intercepting the same events.
Load testing. For modules with large data volumes: benchmarks at 10K, 100K, 1M records. Profiling via Xdebug for memory leaks and N+1 queries.
Case Studies
Promotions module for an electronics chain. The standard sale module discounts didn't cover "buy 2 get 1 free," gift with purchase over a certain amount, or combined conditions. We built a visual constructor: the marketer creates rules via drag-and-drop, without filing development tickets. Promotion calendar, auto-deactivation via agents, analytics linked to b_sale_order — conversion, average order value, usage count. Time to launch a new promotion dropped from two days to half an hour.
Calculator for a construction company. Parameters (area, materials, number of floors) → formula → preliminary estimate → CRM lead via CRest::call('crm.lead.add'). Regional coefficients and seasonal surcharges from a Highload block, material prices from 1C data exchange. Qualified leads increased by a third: clients see a breakdown by line items before calling the sales manager.
Booking system for a hotel chain. Real-time availability via AJAX requests to a custom vendor_booking_slots table, seasonal rate calculations, synchronization with Booking.com via channel manager API. Room locking during concurrent bookings — via SELECT ... FOR UPDATE in a transaction. Timezones handled via \DateTimeZone — a guest from Vladivostok and a manager from Moscow see the same picture.







