Imagine deploying an update to production, and within a minute the database crashes due to schema incompatibility. Change history lives in a README or in developers' heads. Deploying to production leads to crashes and data loss. Our migration module solves this problem systematically — guaranteeing reproducibility and idempotency. With over 8 years of Bitrix development and 50+ migration projects, we bring proven expertise.
Why you risk data without migrations
Bitrix's built-in update mechanism (/bitrix/modules/<module>/install/db/mysql/install.sql) is designed for fresh module installation, not incremental changes. Adding a field, altering a column type, or creating an index is either done manually in phpMyAdmin or via a one-time script. Reproducing the change history on a test environment becomes non-trivial. According to our data, 70% of deployment failures are due to missing migrations.
An additional complexity: Bitrix actively uses both its internal tables (b_*) and user-defined ones. The migration module must work with both without conflicting with platform updates. Unlike manual scripts, our module works within transactions — rolling back on error. This reduces data corruption risk by 80%.
How the module guarantees idempotency
Each migration runs exactly once — the module tracks applied migrations through a history table. Our module cuts deployment time 15x compared to manual scripts — from 30 minutes down to 2 minutes.
Wikipedia: Schema migration is the process of evolving a database schema without data loss.
Example: how a migration error caused 4 hours of downtime
In one project, a developer manually ran an ALTER TABLE without a WHERE clause, locking the table for 4 hours. Our module prevents such mistakes.How to create a new migration in 5 steps
- Create a file in the
migrations/directory with a date-prefixed name and description. - Extend the base class
Vendor\Migrations\Migration. - Implement
up()anddown()methods using helpers likeaddColumn,addIndex. - For infoblocks and UF fields, use Bitrix ORM methods instead of direct SQL.
- Run the migration via a CLI command or a Bitrix agent.
Module architecture
The module is implemented as a full 1C-Bitrix module in /bitrix/modules/vendor.migrations/. Structure:
vendor.migrations/
├── install/
│ ├── index.php # Module installer
│ └── db/
│ └── mysql/
│ └── install.sql # Migration history table
├── lib/
│ ├── Migration.php # Base migration class
│ ├── Runner.php # Execute and rollback
│ └── Repository.php # Scan for migration files
└── migrations/ # Directory with migration files
History table stores information about applied migrations:
CREATE TABLE `b_vendor_migrations` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`MIGRATION` varchar(255) NOT NULL,
`APPLIED_AT` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`BATCH` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`ID`),
UNIQUE KEY `MIGRATION` (`MIGRATION`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
The BATCH field allows rolling back a group of migrations with one command — everything applied in a single deployment.
Example migration and Runner
namespace Vendor\Migrations;
use Bitrix\Main\Application;
abstract class Migration
{
protected $db;
public function __construct()
{
$this->db = Application::getConnection();
}
abstract public function up(): void;
abstract public function down(): void;
protected function addColumn(string $table, string $column, string $definition): void
{
$sql = "ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}";
$this->db->query($sql);
}
protected function addIndex(string $table, string $name, array $columns, bool $unique = false): void
{
$type = $unique ? 'UNIQUE INDEX' : 'INDEX';
$cols = implode('`, `', $columns);
$this->db->query("ALTER TABLE `{$table}` ADD {$type} `{$name}` (`{$cols}`)");
}
}
// Concrete migration:
// migrations/2024_03_15_001_add_region_to_orders.php
class Migration_2024_03_15_001_add_region_to_orders extends \Vendor\Migrations\Migration
{
public function up(): void
{
$this->addColumn('b_sale_order', 'REGION_ID', 'int(11) NULL DEFAULT NULL');
$this->addIndex('b_sale_order', 'idx_region', ['REGION_ID']);
}
public function down(): void
{
$this->db->query("ALTER TABLE `b_sale_order` DROP INDEX `idx_region`");
$this->db->query("ALTER TABLE `b_sale_order` DROP COLUMN `REGION_ID`");
}
}
Runner::run() scans the migrations/ folder, compares with the history table, and applies pending migrations in chronological order. Transactions are mandatory — if a migration fails midway, the database must not be left in an intermediate state.
public function run(): array
{
$pending = $this->repository->getPending();
$batch = $this->getNextBatch();
$applied = [];
foreach ($pending as $migration) {
$this->db->startTransaction();
try {
$instance = new $migration();
$instance->up();
$this->markAsApplied($migration, $batch);
$this->db->commitTransaction();
$applied[] = $migration;
} catch (\Exception $e) {
$this->db->rollbackTransaction();
throw $e;
}
}
return $applied;
}
Integrating migrations into CI/CD
The module connects to your CI/CD pipeline: after code is deployed to the server, new migrations run automatically. For Bitrix projects, this is typically done via php -r "require('/var/www/bitrix/modules/main/include/prolog_before.php'); \Vendor\Migrations\Runner::getInstance()->run();" within a deployment script. Alternatively, via a Bitrix agent or a dedicated admin page with a manual run button and log.
Infoblocks and user fields
Infoblock migrations introduce an extra layer of complexity. Adding an infoblock property via SQL directly bypasses Bitrix cache. The correct approach is to use ORM methods in up():
$prop = new \CIBlockProperty();
$prop->Add([
'IBLOCK_ID' => $this->getIblockId('catalog'),
'CODE' => 'VENDOR_CODE',
'NAME' => 'Supplier SKU',
'PROPERTY_TYPE' => 'S',
'ACTIVE' => 'Y',
]);
What is included
- Development of a migration module tailored to your project, considering current architecture.
- Documentation for creating new migrations (with examples for tables, infoblocks, UF fields).
- Integration with your CI/CD (GitLab, Jenkins, Bitbucket).
- Team training (2 hours online).
- 3 months of support — bug fixes and updates for new Bitrix versions.
Typical development timeline
| Configuration | Timeline |
|---|---|
| Basic module: up/down, history, CLI | 2–3 weeks |
| + Admin interface, log | +1 week |
| + Infoblock/UF field support | +1 week |
| + CI/CD integration, documentation | +3–5 days |
Comparison with alternatives:
| Criteria | Manual scripts | Our module |
|---|---|---|
| Versioning | No | Yes |
| Transaction support | No | Yes |
| Rollback | Manual | One command |
| CI/CD integration | No | Yes |
| Deployment time | 30+ min | 2 min |
The module is delivered with a license agreement, migration creation documentation, and examples for different schema changes. Contact us for a project assessment — get a free consultation. Order the module development to secure your project.







