D7 API Class Development for 1C-Bitrix — A Modern Approach

Does the outdated core slow down your site and break updates? We develop D7 API classes turnkey: we migrate logic to the modern layer, speed up the catalog, and eliminate the risk of failures during updates. We work in stages, with documentation and a guaranteed result.

Our competencies:

Frequently Asked Questions

Why legacy handlers in 1C-Bitrix break updates

Legacy handlers in 1C-Bitrix rely on old-core events and direct database queries that bypass the standard mechanisms. As long as the platform isn't touched, the scheme works for years.

But as soon as a core update is released, the old code stops finding its usual entry points: methods have been renamed, part of the logic has been moved into new classes, and the handler keeps being called by its old name and silently returns nothing.

Outwardly, this looks like "the site is up, but the cart doesn't calculate the discount" — the most expensive class of failures, because it isn't noticed right away.

Module conflicts manifest differently. Two extensions subscribe to the same event and modify the same object one after the other — the result depends on the load order.

Update either module, and the order changes: prices are recalculated twice, emails go out in duplicates, order statuses jump around. Debugging such a conflict takes days, because it only reproduces on a specific combination of versions.

Migration errors hit the data. Direct queries to tables, bypassing the D7 API, know nothing about the new mandatory fields and relations that appeared in the updated core.

The write succeeds, but the object remains inconsistent: a product is in the catalog but can't be found in search, an order is created but isn't visible in the report.

Cleaning this up requires scripts running against the live database — with the risk of touching neighboring entities.

The D7 API closes off these failures at the architecture level: events are registered through the standard mechanism, work with entities goes through core objects rather than through SQL, and a platform update doesn't break the connections.

Let's break down what this looks like in code and what it gives a project with every new core version.

What Migrating to D7 API Delivers: Measurable Results

Migrating to D7 API changes not the "style of the code," but the behavior of the system under load. The old core approach with CIBlockElement::GetList and global variables drags along extra database queries and a cache that cannot be invalidated in a targeted way.

D7 API provides an object model where queries are built through Query, and the result — through Collection with lazy loading of relations.

In practice, this eliminates duplicate queries on catalog lists and in sections with filters: a page that used to be assembled from dozens of database calls now fits into a single query with explicitly specified fields.

Code reliability grows out of predictability. In D7 API, entities are typed, and events are registered through EventManager, rather than through string handler names that are easy to lose during refactoring.

Access and permission errors surface at the development stage, not in production as a blank page. This directly reduces the share of failures related to human error: the developer sees the method contract rather than guessing at it from documentation.

Faster development here is a consequence, not a goal in itself. When queries, permissions, and caching are described uniformly, new functionality fits into existing patterns without copying legacy blocks.

Maintenance boils down to editing a specific class, rather than doing archaeology through component.php with patches of the old core. For projects on 1C-Bitrix, this means that customizations for 1C and Bitrix24 do not break already working sections.

Let's break down how this is addressed on a specific project: from auditing current queries to measuring response time before and after.

ORM, events, and caching: data handling modes

Data handling in the D7 API is built on three independent mechanisms, and confusing them is the most common cause of "slow" code.

ORM is responsible for fetching and writing, events are responsible for reacting to changes, and cache is responsible for how many times all of this actually executes. Below is how these modes combine in practice.

Where each mode is appropriate

Mode When it fits Limitations
ORM Fetches across related entities, pagination, filtered selection Heavy joins on large tables require indexes, otherwise the query degrades
Events Reacting to an entity change: recalculation, notification, synchronization with 1C A handler inside a save loop causes a multiple increase in response time
Cache Reference data, catalogs, rarely changing selections Tag-based invalidation: without it, data "sticks" after an update

ORM in 1C-Bitrix is not a wrapper over SQL, but a layer with lazy loading of relations. If you pull related entities in a loop, you get the classic N+1 problem: instead of one query, a hundred are sent. This is cured by selecting the required fields and eager-loading relations in advance.

Events provide loose coupling: a module subscribes to an entity change and does not know who else reacts to it. The danger is that the handler executes synchronously — heavy logic inside it slows down saving for everyone. We move it to deferred processing, leaving only task enqueueing in the event.

Caching closes the issue of repeated computations. The key point is tags: a reference data cache is marked with the entity tag, and when it changes, the cache is reset automatically.

Without tags, you have to clear the cache manually, and sooner or later someone will forget — the data will diverge.

We analyze on your project which selections go to ORM, what we move to deferred handlers, and what we cover with tagged caching.

How We Implement the D7 API: Project Phases

Migrating to the D7 API is not rewriting for the sake of fashion, but a managed migration: the old code based on CIBlockElement and CSaleOrder continues to work, while new code is already written using core classes.

We run the project in phases so that at every step the system remains operational and rollback-ready.

Audit and Dependency Map

First, we build an inventory: which components, modules, and agents rely on legacy calls, where logic is duplicated, and which areas are not covered by tests at all.

The output is a dependency map with priorities: what we migrate first, what can be left as is, and what is cheaper to rewrite from scratch.

Migration and Development

  1. Code audit and dependency map — we identify entry points, scope, and risks.
  2. Layer design — we separate business logic, data access, and presentation so that the code does not depend on specific modules.
  3. Phased migration — we move one area at a time, with old and new code coexisting through adapters.
  4. Development on the D7 API — we write classes, services, and event handlers instead of direct database queries.
  5. Testing — we cover critical paths with automated tests and run regression on a copy of the production database.
  6. Rollout and monitoring — release in parts, with logs and quick rollback to the previous version.
  7. Handover — architecture documentation and instructions for the client's team.

Why Phases Instead of a Single Release

A large migration in a single commit almost always breaks hidden connections between modules. The step-by-step approach provides predictability: each phase is verified separately, and the business continues to operate without downtime.

After handover, the team is left with code that can be developed without fear of affecting a neighboring module.

What you get: classes, schemas, documentation

We hand over not a "working website," but a system that your team can evolve without us. Below is exactly what remains in your hands after the project is delivered.

  • D7 classes and source code. Modules on the current kernel branch: services, repositories, event handlers, agents. Code in git with commit history, not an archive — you can see who changed the logic and why.
  • Data schemas. A map of information blocks, highload reference books, custom tables, and the relationships between them: where state is stored, what is denormalized and why. The schema removes the main support risk — "why are there two sources of truth here."
  • 1C exchange regulations. A description of formats, directions, and synchronization frequency, behavior on discrepancies and repeated uploads. Separately — what to do on a record version conflict.
  • Configuration documentation. Settings for caching, queues, access rights, and environments. With an indication of which parameters change in production and which are intentionally fixed.
  • Deployment instructions. The rollout order, schema migrations, rollback points to the previous release. Your engineer performs the deploy using it without our involvement.
  • A set of test scenarios. Checks of key paths: exchange, order placement, rights. They are run before release so that regressions are caught before production.
  • A decision log. Why this particular data access layer was chosen, where technical debt was deliberately left and what to do about it next.

The package is designed for handover: a new developer enters the project through the documentation, not through correspondence with the author. Describe the task — we will come back with an estimate and a plan, including the composition of these artifacts for your environment.

Case study: 3x faster catalog loading after switching to the D7 API

A catalog with 42,000 items, six levels of nesting, and stock across four warehouses took 11 seconds to load: the category page was assembled from hundreds of sequential queries to information blocks, while prices and availability were pulled in by separate calls already in the template.

Under peak load, response time reached 19 seconds, search engines dropped the positions, and some visitors left before the page finished rendering.

The audit revealed three sources of loss: cyclic queries through the old core interface without caching, duplicate property queries on every product, and the absence of a unified data access layer.

We rewrote the catalog query using the D7 API: we assembled queries through the object model with explicit specification of fields and relations, moved stock and prices into a single batch query, and applied managed cache with tags to information blocks and warehouses.

Invalidation now triggers precisely — when stock for a warehouse changes, only the related part is reset, not the entire section cache.

1С-Bitrix provides predictable behavior in this scenario: tagged cache and object queries work at the core level, so the logic does not have to be worked around with third-party add-ons.

Metric Before After
Category load 11 s 3.4 s
Response under load 19 s 5.1 s
Database queries per page 340+ 27
Cache hit rate 0% 86%

Additionally, we reduced the volume of transmitted data: selecting fields instead of SELECT * cut the database response several times over, and heavy properties are loaded lazily.

The catalog withstood the seasonal peak without degradation, and rolling back to the previous logic remained possible — the old calls are preserved in a separate branch for the transition period.

How D7 API differs from the old core and what to do during migration

The old core was built on global variables, direct SQL queries, and inheritance through CModule — the code relied on side effects and the order in which files were included.

D7 API moves the same functionality onto an object model: entities are described by classes with a clear area of responsibility, data access goes through an ORM layer on top of tables, and events and cache are connected declaratively rather than "by hand" in every call.

The practical difference shows up in predictability: in the old core, behavior depended on what had managed to execute earlier, while in D7 dependencies are explicit, and the same code produces the same result regardless of the entry point.

The second difference is data manageability. ORM in D7 provides queries with filters, relations, and pagination without manually stitching queries together, while caching is tied to a specific query and reset in a targeted way rather than entirely by module tag.

In practice, this removes a classic pain point: a page stops "getting stuck" on stale cache after a single entity is edited, and heavy lists don't pull the entire table just for a couple of fields.

Migration is not rewriting for the sake of rewriting, but a transition strategy. Mixing both cores in one project is possible and perfectly fine: the old core keeps working, new functionality is written directly on D7, and legacy is migrated as it is touched.

The order is usually this: first we isolate business logic from calls to the old core, then we move it to D7 classes, then we connect ORM and declarative cache, and only after that do we clean up direct queries.

Such a phased transition does not stop development and does not require a "big release" after which everything must work at once.

The key risk of migration is not syntax but implicit dependencies: somewhere the logic is tied to global variables, somewhere to the side effect of a component being included.

That is why it is worth migrating with critical scenarios covered by tests — then a change of core under the hood does not change behavior for the user.

If you have accumulated code on the old core and it is unclear where to start the transition, describe the task, and we will come back with an estimate and a phased migration plan.

Common technical concerns when working with the D7 API

Migrating to D7 is not a rewrite for the sake of fashion, but a change in the data handling model: instead of global arrays and manual database queries, you get entities, cache, and events with predictable behavior.

Below are the questions most often asked before starting, and how these risks are addressed in practice.

Will legacy code and D7 conflict?

No, they coexist within the same project. Legacy modules continue to work through their previous calls, while new functionality is written on D7.

Conflicts arise not from coexistence, but from mixing layers within a single handler — which is why we delineate areas of responsibility and document the boundaries.

What about cache and performance?

D7 provides managed cache with tagging: when an entity changes, only the related layer is invalidated, not the entire page cache.

This eliminates the classic problem of "changed one product — the whole catalog went stale." On high-load sections, page response time drops severalfold, and database load stops growing linearly with traffic.

How does migration proceed without downtime?

We move functionality module by module, not in a single release: first entities and events, then the public-facing part. Each stage is verified on a test environment while production remains operational.

Rollback means reverting to the previous module version, without restoring the database from a backup.

Who will maintain the code after handover?

D7 is a platform standard, not our custom layer, so the code can be read by any Bitrix developer. We hand over entity schemas, event descriptions, and cache handling rules — maintenance does not depend on a specific person. If needed, we take the project on for support and further development.

How risky is this for the current project?

The risk is manageable: changes go through version control, each affects a limited area, and the test environment mirrors the production configuration. Before release, we run through checkout, payment, and authorization scenarios — the paths where an error is most costly.

These questions are resolved not by promises, but by the order of work: module boundaries, a test environment, a phased release. Describe your task — we will come back with an estimate and a migration plan.

Let's discuss your D7 API development task

D7 API development doesn't start with code — it starts with breaking down the task: which entities already exist in the project, what lives in legacy code, and where the boundaries with 1C and external systems lie.

At this stage, it's important to understand whether we're talking about new modules on the D7 API, migrating existing logic off the outdated core, or making targeted improvements — the scope of work and the order of steps depend on this.

We ask clarifying questions about infrastructure, load, and usage scenarios so that the plan is based on facts, not assumptions.

To make the discussion of the task substantive, send us what you already have: a description of the business process, a diagram of the exchange with 1C, access to the repository or a test environment.

If there's no documentation — no problem, our engineers will reconstruct the picture from the code and logs. The more context you provide upfront, the more accurate the estimate and the fewer surprises during implementation.

From there, the work follows clear steps:

  • We analyze the current state of the project: module structure, dependencies, bottlenecks in legacy code.
  • We pin down the requirements for the new logic on the D7 API: entities, events, handlers, integrations.
  • We prepare a work plan with stages, timelines, and checkpoints — no vague wording.
  • We agree on the architecture: how the new D7 API classes coexist with the old core without breaking existing functionality.
  • We define the migration and rollback strategy so that updates are released predictably.
  • We estimate the scope, team composition, and the order in which results are handed over.
  • We agree on the post-launch support format: procedures, communication channels, incident response.

Describe your task — we'll come back with an estimate and a work plan. If needed, we'll start with a technical review of the current code and suggest which areas make sense to migrate to the D7 API first.