Рефакторинг C++ в OpenBMC: как ИИ боролся с 280-строчной функцией
Функция createSensors из проекта OpenBMC (открытой прошивке для BMC серверов) собирает 280 строк код и выполняет 12 разных задач: получает конфигурацию от entity-manager асинхронно в одном потоке, ищет I2C-драйверы через sysfs, проверяет присутствие устройств на шине, сопоставляет с конфигурацией, создает и регистрирует объекты HwmonTempSensor на D-Bus, управляет порогами срабатывания, частотой опроса, состоянием питания, логикой нескольких датчиков на одном устройстве, поддерживает полный скан при старте и точечное обновление конфигурации. ИИ-агент предложил разбить функцию на 7 конкретных шагов: выделить класс SensorScanner с методами buildConfigMap, findSensorPaths, ensureDevice, processPath и matchConfig; устранить копипаст при создании датчиков (60 строк дублирования); вынести имена в структуры матчинга; разделить логику скана от обновления; изолировать side-эффекты; заменить глубокую вложенность на ранние continue. • **Результат рефакторинга**: вложенность (6–7 уровней if/for) ушла, функции уменьшились до 20–30 строк, появилась явная структура. Но git-diff показал неожиданное — зелёного кода (новое) стало больше, чем красного (удалено). Дополнены сигнатуры 10 методов, новые структуры и классы, которые скомпенсировали сэкономленные 60 строк копипаста. • **Архитектурная беда**: микросервисы OpenBMC не читают конфиги напрямую — это делает центральный сервис entity-manager, выкладывающий данные на D-Bus порционно. Наш сервис не знает, когда конфигурация готова (нет транзакций), поэтому используется тайм-аут на 1 секунду (filterTimer.expires_after(std::chrono::seconds(1))). Флаги firstScan, sensorsChanged, activateOnly в createSensors существуют именно поэтому — отследить порционные обновления.
AI-обработка оригинала Habr AI; редакция Hamidun News
На что способен ИИ-агент при рефакторинге
Разработчик проанализировал микросервис HwmonTempSensor из OpenBMC — проекта открытой прошивки для модулей управления серверами на базе Linux. Весь сервис сосредоточен вокруг одной функции createSensors, которая собирает показания с аппаратных датчиков (температуры, давления, влажности, тока, напряжения) и публикует их через D-Bus шину для других сервисов, например веб-интерфейса.
Функция на 280 строк выполняет 12 изолированных задач: получает конфигурацию от entity-manager асинхронно, но в одном потоке; ищет драйверы I2C через sysfs; проверяет присутствие устройств на шине; сопоставляет конфигурацию с найденными устройствами; создает, активирует или пересоздает объекты HwmonTempSensor; регистрирует их на D-Bus; управляет порогами срабатывания; управляет частотой опроса; управляет состоянием питания; реализует логику работы нескольких датчиков на одном I2C-устройстве; поддерживает полный первичный скан при старте; обрабатывает точечное обновление при изменении конфигурации на лету.
Вложенность циклов и условных операторов достигает 6–7 уровней (for → for/if → if → if → if). Когда разработчик попросил ИИ проанализировать проблемы, агент быстро выдал диагноз: перегруженность функции, спагетти-логика из разнородных операций (поиск, матчинг, создание, фильтрация), полная нетестируемость.
7 конкретных шагов рефакторинга
Агент предложил выделить класс SensorScanner, где каждый шаг станет приватным методом на 20–30 строк:
Шаг 1: создать struct SensorScanner с конструктором, принимающим параметры io, objectServer, sensors, dbusConnection, sensorsChanged, activateOnly; один публичный метод scan как единственная входная точка обработки конфигурации.
Шаг 2: разбить логику на приватные методы: buildConfigMap (парсинг конфигурации), findSensorPaths (поиск sysfs-путей), ensureDevice (инстанцирование I2C-устройств), processPath (обработка одного sysfs-пути в цикле), matchConfig (сопоставление пути с конфигурацией).
Шаг 3: устранить дублирование при создании датчиков. В оригинальном коде логика создания HwmonTempSensor продублирована дважды (для основного датчика и дополнительных). Вынести в метод createOrActivateSensor с параметрами hwmonFile, i2cDev и другие. Это сэкономило 60 строк копипаста.
Шаги 4–7: вынести имена Name1..N в отдельные структуры матчинга; разделить логику полного скана от логики обновления конфигурации; изолировать все side-эффекты в отдельные методы; заменить глубокую вложенность if на ранние continue.
Парадокс результата: рефакторинг сделал код красивей, но не короче
После реализации предложений функция createSensors стала одной строкой:
```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ```
Vложенность ушла, методы стали маленькими, структуры явными. На собеседованиях требуют именно такой код: C++20, шаблоны, RAII, Structured binding.
Но git-diff выдал неожиданное: зелёного кода (новые строки) стало заметно больше, чем красного (удалено). Общий объем кода вырос, хотя копипаст был найден и выпилен. Причина? 10 новых методов требуют 10 сигнатур; новые структуры и классы (чтобы не передавать под десяток параметров по ссылке); определения для изоляции логики. Страшный код просто стал по-модному структурированным.
Главное откровение: нельзя отрефакторить одну функцию
Вот здесь и начинается боль. Ужас конкретной функции — не ошибка уставшего программиста и не случайный сбой. Это прямое, неизбежное следствие убогости архитектурных идей всего проекта.
OpenBMC гордится Key Features: Runtime re-configurable from d-bus; Isolated: each sensor type is isolated into its own daemon; Async single-threaded: uses sdbusplus/asio bindings. Звучит современно и масштабируемо.
Реальность иная. Вместо простого алгоритма (читай JSON-конфиг → найди устройства → работай) авторы разделили неделимый процесс на микросервисы. Отдельные сервисы датчиков не читают конфигурационные JSON-файлы — это делает центральный сервис entity-manager. Он парсит ВСЕ конфиги и выкладывает данные на D-Bus... постепенно! Все микросервисы вынуждены ждать, пока их нужная часть конфигурации будет опубликована.
Главная проблема: нет гарантии атомарности. Наш сервис не знает, когда entity-manager закончит вещать данные для всех устройств в системе. Разработчики D-Bus и микросервисных концепций в embedded никогда не слышали про транзакции.
Решение: тайм-аут на глаз. Пока entity-manager шлет куски конфига, фильтр постоянно сбрасывает счетчик. Выставляется задержка в 1 секунду (filterTimer.expires_after(std::chrono::seconds(1))). Если целую секунду новых данных не прилетало — считаем, что конфигурация готова, вызываем createSensors.
Именно из-за этой архитектурной убогости функция createSensors превратилась в ад. Ей приходится на лету разбираться с флагами firstScan, sensorsChanged и activateOnly, потому что данные прилетают порциями, а система должна правильно запомнить свое состояние и реагировать на обновления.
Вывод: это Сизифов труд. Нельзя сделать рефакторинг одной, сколь угодно страшной С++ функции и ожидать, что это спасет проект. Код здесь — не первопричина, это просто реализация абстрактных концепций, которые кто-то притащил в embedded, не подумав о деталях.
- 27 окт. 2026 г.Fudan и Huawei Xiaoyi научили ИИ-агента писать операторы для Ascend NPU: 95,5% успеха
- 27 окт. 2026 г.EmbodiedLab: роботов проверили на экспериментах, и лучший решил меньше половины
- 27 окт. 2026 г.Skill∞: агент сам улучшает навыки и свой метанавык, 60,7% на SkillsBench
- 27 окт. 2026 г.Work Agents: 16 исследователей собрали единую систему координат для ИИ-агентов
Вопросы и ответы по материалу
Что известно по теме: на что способен ИИ-агент при рефакторинге?
Разработчик проанализировал микросервис HwmonTempSensor из OpenBMC — проекта открытой прошивки для модулей управления серверами на базе Linux. Весь сервис сосредоточен вокруг одной функции createSensors, которая собирает показания с аппаратных датчиков (температуры, давления, влажности, тока, напряжения) и публикует их через D-Bus шину для других сервисов, например веб-интерфейса. Функция на 280 строк выполняет 12 изолированных задач: получает конфигурацию от entity-manager асинхронно, но в одном потоке; ищет драйверы I2C через sysfs; проверяет присутствие устройств на шине
Что известно по теме: 7 конкретных шагов рефакторинга?
Агент предложил выделить класс SensorScanner, где каждый шаг станет приватным методом на 20–30 строк: Шаг 1: создать struct SensorScanner с конструктором, принимающим параметры io, objectServer, sensors, dbusConnection, sensorsChanged, activateOnly; один публичный метод scan как единственная входная точка обработки конфигурации. Шаг 2: разбить логику на приватные методы: buildConfigMap (парсинг конфигурации), findSensorPaths (поиск sysfs-путей), ensureDevice (инстанцирование I2C-устройств), processPath (обработка одного sysfs-пути в цикле), matchConfig (сопоставление пути с
Что известно по теме: парадокс результата: рефакторинг сделал код красивей, но не короче?
После реализации предложений функция createSensors стала одной строкой: ```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ``` Vложенность ушла, методы стали маленькими, структуры явными. На собеседованиях требуют именно такой код: C++20, шаблоны, RAII, Structured binding. Но git-diff выдал неожиданное: зелёного кода (новые строки) стало заметно больше, чем красного (удалено). Общий объем кода вырос, хотя копипаст был найден и выпилен. Причина? 10 новых методов требуют 10 сигнатур; новые структуры и классы (чтобы не перед
Соберите рабочего ИИ-агента или сервис своими руками без ручного кода
В Академии Жемала Хамидуна (academy.hamidun.com) открыт бесплатный 72-часовой тест-драйв: интерактивные скетч-уроки по Claude Code и Cursor, живой Tech Radar и готовые шаблоны агентов.
Главное из мира ИИ — раз в неделю
7 ключевых событий недели, отобранных вручную. Без шума, репостов и пресс-релизов.
Готово! Проверьте почту — мы отправили подтверждение.