Refactoring C++ dans OpenBMC : comment l'IA s'est débattue avec une fonction de 280 lignes
La fonction createSensors du projet OpenBMC (firmware ouvert pour les BMC de serveurs) pèse 280 lignes de code et accomplit 12 tâches différentes : elle récupère la configuration auprès d'entity-manager de façon asynchrone dans un seul thread, recherche les pilotes I2C via sysfs, vérifie la présence des périphériques sur le bus, les met en correspondance avec la configuration, crée et enregistre des objets HwmonTempSensor sur D-Bus, gère les seuils de déclenchement, la fréquence de scrutation, l'état d'alimentation, la logique de plusieurs capteurs sur un même périphérique, et prend en charge le scan complet au démarrage ainsi que la mise à jour ciblée de la configuration. L'agent IA a proposé de découper la fonction en 7 étapes concrètes : extraire une classe SensorScanner avec les méthodes buildConfigMap, findSensorPaths, ensureDevice, processPath et matchConfig ; éliminer le copier-coller dans la création des capteurs (60 lignes de duplication) ; déplacer les noms dans des structures de correspondance ; séparer la logique de scan de celle de mise à jour ; isoler les effets de bord ; remplacer l'imbrication profonde par des continue anticipés. • **Résultat du refactoring** : l'imbrication (6 à 7 niveaux de if/for) a disparu, les fonctions sont passées à 20-30 lignes, une structure explicite est apparue. Mais le git-diff a révélé une surprise : le code vert (ajouté) est devenu plus volumineux que le rouge (supprimé). Les signatures de 10 méthodes, de nouvelles structures et classes ont plus que compensé les 60 lignes de copier-coller économisées. • **Le mal architectural** : les microservices d'OpenBMC ne lisent pas directement les configurations — c'est un service central, entity-manager, qui s'en charge et publie les données sur D-Bus par morceaux. Notre service ne sait pas quand la configuration est prête (il n'y a pas de transactions), d'où l'usage d'un timeout d'une seconde (filterTimer.expires_after(std::chrono::seconds(1))). Les drapeaux firstScan, sensorsChanged et activateOnly dans createSensors existent précisément pour suivre ces mises à jour par morceaux.
Traité par IA depuis Habr AI ; édité par Hamidun News
Ce dont une IA agent est capable lors d'un refactoring
Un développeur a analysé le microservice HwmonTempSensor d'OpenBMC — un projet de firmware ouvert pour les modules de gestion de serveurs sous Linux. Tout le service tourne autour d'une seule fonction, createSensors, qui collecte les mesures des capteurs matériels (température, pression, humidité, courant, tension) et les publie via le bus D-Bus pour d'autres services, comme l'interface web.
Cette fonction de 280 lignes accomplit 12 tâches isolées : elle récupère la configuration auprès d'entity-manager de façon asynchrone, mais dans un seul thread ; elle recherche les pilotes I2C via sysfs ; elle vérifie la présence des périphériques sur le bus ; elle met en correspondance la configuration avec les périphériques trouvés ; elle crée, active ou recrée des objets HwmonTempSensor ; elle les enregistre sur D-Bus ; elle gère les seuils de déclenchement ; elle gère la fréquence de scrutation ; elle gère l'état d'alimentation ; elle implémente la logique de plusieurs capteurs sur un même périphérique I2C ; elle prend en charge le scan complet initial au démarrage ; elle traite la mise à jour ciblée lors d'un changement de configuration à chaud.
L'imbrication des boucles et des conditions atteint 6 à 7 niveaux (for → for/if → if → if → if). Quand le développeur a demandé à l'IA d'analyser les problèmes, l'agent a livré un diagnostic rapide : surcharge de la fonction, logique spaghetti mêlant des opérations hétérogènes (recherche, correspondance, création, filtrage), absence totale de testabilité.
7 étapes concrètes de refactoring
L'agent a proposé d'extraire une classe SensorScanner, où chaque étape devient une méthode privée de 20-30 lignes :
Étape 1 : créer une struct SensorScanner avec un constructeur recevant les paramètres io, objectServer, sensors, dbusConnection, sensorsChanged, activateOnly ; une seule méthode publique scan comme unique point d'entrée pour le traitement de la configuration.
Étape 2 : scinder la logique en méthodes privées : buildConfigMap (analyse de la configuration), findSensorPaths (recherche des chemins sysfs), ensureDevice (instanciation des périphériques I2C), processPath (traitement d'un chemin sysfs dans la boucle), matchConfig (mise en correspondance d'un chemin avec la configuration).
Étape 3 : éliminer la duplication lors de la création des capteurs. Dans le code d'origine, la logique de création de HwmonTempSensor est dupliquée deux fois (pour le capteur principal et les capteurs secondaires). La déplacer dans une méthode createOrActivateSensor avec des paramètres hwmonFile, i2cDev et autres. Cela a permis d'économiser 60 lignes de copier-coller.
Étapes 4 à 7 : extraire les noms Name1..N dans des structures de correspondance dédiées ; séparer la logique du scan complet de celle de la mise à jour de configuration ; isoler tous les effets de bord dans des méthodes séparées ; remplacer l'imbrication profonde de if par des continue anticipés.
Le paradoxe du résultat : le refactoring a rendu le code plus beau,
mais pas plus court
Une fois ces propositions mises en œuvre, createSensors s'est réduite à une seule ligne :
```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ```
L'imbrication a disparu, les méthodes sont devenues petites, les structures explicites. C'est exactement ce genre de code qu'on exige en entretien : C++20, templates, RAII, structured binding.
Mais le git-diff a révélé une surprise : le code vert (les nouvelles lignes) est devenu nettement plus volumineux que le rouge (supprimé). Le volume total de code a augmenté, même si le copier-coller a bien été repéré et éliminé. La raison ? 10 nouvelles méthodes nécessitent 10 signatures ; de nouvelles structures et classes (pour éviter de passer une dizaine de paramètres par référence) ; des définitions supplémentaires pour isoler la logique. Le code effrayant est simplement devenu structuré à la mode.
La vraie révélation : on ne peut pas refactorer une seule fonction
C'est là que la douleur commence vraiment. L'horreur de cette fonction précise n'est ni une erreur de programmeur fatigué ni un accident isolé. C'est la conséquence directe et inévitable de la pauvreté des idées architecturales de tout le projet.
OpenBMC affiche fièrement ses 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. Ça sonne moderne et scalable.
La réalité est tout autre. Au lieu d'un algorithme simple (lire la config JSON → trouver les périphériques → travailler), les auteurs ont découpé un processus indivisible en microservices. Les services de capteurs individuels ne lisent pas les fichiers de configuration JSON — c'est le service central entity-manager qui s'en charge. Il parse TOUTES les configurations et publie les données sur D-Bus... progressivement ! Tous les microservices sont contraints d'attendre que la portion de configuration qui les concerne soit publiée.
Le problème principal : aucune garantie d'atomicité. Notre service ne sait pas quand entity-manager aura fini de diffuser les données pour tous les périphériques du système. Les développeurs du D-Bus et des concepts de microservices en embarqué n'ont visiblement jamais entendu parler des transactions.
La solution : un timeout approximatif. Tant qu'entity-manager envoie des morceaux de configuration, le filtre réinitialise constamment son compteur. Un délai d'une seconde est fixé (filterTimer.expires_after(std::chrono::seconds(1))). Si aucune nouvelle donnée n'est arrivée pendant une seconde entière — on considère la configuration comme prête, et on appelle createSensors.
C'est précisément à cause de cette pauvreté architecturale que la fonction createSensors s'est transformée en enfer. Elle doit gérer à la volée les drapeaux firstScan, sensorsChanged et activateOnly, parce que les données arrivent par morceaux et que le système doit correctement mémoriser son état et réagir aux mises à jour.
Conclusion : c'est un travail de Sisyphe. On ne peut pas refactorer une seule fonction C++, aussi effrayante soit-elle, et espérer sauver le projet pour autant. Le code ici n'est pas la cause première, c'est simplement l'implémentation de concepts abstraits que quelqu'un a importés dans l'embarqué sans réfléchir aux détails.
- 27 oct. 2026Fudan et Huawei Xiaoyi apprennent à un agent IA à écrire des opérateurs pour les NPU Ascend : 95,5 % de réussite
- 27 oct. 2026EmbodiedLab : des robots testés sur des expériences, et le meilleur en réussit moins de la moitié
- 27 oct. 2026Skill∞ : un agent améliore lui-même ses compétences et sa métacompétence, 60,7 % sur SkillsBench
- 27 oct. 2026Work Agents : 16 chercheurs établissent un système de coordonnées commun pour les agents IA
Questions clés sur cet article
Que sait-on sur Ce dont une IA agent est capable lors d'un refactoring ?
Un développeur a analysé le microservice HwmonTempSensor d'OpenBMC — un projet de firmware ouvert pour les modules de gestion de serveurs sous Linux. Tout le service tourne autour d'une seule fonction, createSensors, qui collecte les mesures des capteurs matériels (température, pression, humidité, courant, tension) et les publie via le bus D-Bus pour d'autres services, comme l'interface web. Cette fonction de 280 lignes accomplit 12 tâches isolées : elle récupère la configuration auprès d'entity-manager de façon asynchrone, mais dans un seul thread ; elle recherche les pilo
Que sait-on sur 7 étapes concrètes de refactoring ?
L'agent a proposé d'extraire une classe SensorScanner, où chaque étape devient une méthode privée de 20-30 lignes : Étape 1 : créer une struct SensorScanner avec un constructeur recevant les paramètres io, objectServer, sensors, dbusConnection, sensorsChanged, activateOnly ; une seule méthode publique scan comme unique point d'entrée pour le traitement de la configuration. Étape 2 : scinder la logique en méthodes privées : buildConfigMap (analyse de la configuration), findSensorPaths (recherche des chemins sysfs), ensureDevice (instanciation des périphériques I2C), processP
Que sait-on sur Le paradoxe du résultat : le refactoring a rendu le code plus beau, mais pas plus court ?
Une fois ces propositions mises en œuvre, createSensors s'est réduite à une seule ligne : ```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ``` L'imbrication a disparu, les méthodes sont devenues petites, les structures explicites. C'est exactement ce genre de code qu'on exige en entretien : C++20, templates, RAII, structured binding. Mais le git-diff a révélé une surprise : le code vert (les nouvelles lignes) est devenu nettement plus volumineux que le rouge (supprimé). Le volume total de code a augmenté, même si le cop
Delegate Deep Research, Coding & Workflows to an AI Agent
Deploy autonomous multi-step execution with NomadNet Agent (agent.nomadnet.ai) — automated web research, data synthesis, code generation, and complex task orchestration.
L'essentiel de l'IA — une fois par semaine
Sept actus qui ont vraiment compté, choisies à la main. Sans bruit ni communiqués.
C'est fait ! Vérifiez votre boîte mail pour la confirmation.