Refactorización de C++ en OpenBMC: cómo un agente de IA luchó contra una función de 280 líneas
La función createSensors del proyecto OpenBMC (el firmware abierto para los BMC de servidores) agrupa 280 líneas de código y ejecuta 12 tareas distintas: obtiene la configuración de entity-manager de forma asíncrona pero en un solo hilo, busca drivers I2C a través de sysfs, comprueba la presencia de dispositivos en el bus, los coteja con la configuración, crea y registra objetos HwmonTempSensor en D-Bus, gestiona umbrales de activación, frecuencia de sondeo y estado de energía, maneja la lógica de varios sensores en un mismo dispositivo, y soporta tanto un escaneo completo al arrancar como una actualización puntual de configuración. El agente de IA propuso dividir la función en 7 pasos concretos: extraer una clase SensorScanner con los métodos buildConfigMap, findSensorPaths, ensureDevice, processPath y matchConfig; eliminar el copia-pega en la creación de sensores (60 líneas duplicadas); mover los nombres a estructuras de coincidencia; separar la lógica de escaneo de la de actualización; aislar los efectos secundarios; y sustituir el anidamiento profundo por continues tempranos. • **Resultado de la refactorización**: el anidamiento (6-7 niveles de if/for) desapareció, las funciones se redujeron a 20-30 líneas y surgió una estructura clara. Pero el git diff mostró algo inesperado: hubo más código en verde (añadido) que en rojo (eliminado). Las firmas de los 10 nuevos métodos, junto con nuevas estructuras y clases, compensaron de sobra las 60 líneas de copia-pega ahorradas. • **El problema arquitectónico**: los microservicios de OpenBMC no leen las configuraciones directamente; eso lo hace un servicio central, entity-manager, que publica los datos en D-Bus por partes. El servicio de sensores no tiene forma de saber cuándo la configuración está completa (no hay transacciones), así que recurre a un timeout de 1 segundo (filterTimer.expires_after(std::chrono::seconds(1))). Las banderas firstScan, sensorsChanged y activateOnly en createSensors existen precisamente por esto: para rastrear actualizaciones parciales.
Procesado por IA desde Habr AI; editado por Hamidun News
Qué puede hacer realmente un agente de IA al refactorizar
Un desarrollador analizó el microservicio HwmonTempSensor de OpenBMC, el proyecto de firmware abierto para los módulos de gestión de servidores basados en Linux. Todo el servicio gira en torno a una única función, createSensors, que recoge lecturas de sensores de hardware (temperatura, presión, humedad, corriente, voltaje) y las publica a través del bus D-Bus para que otros servicios, como la interfaz web, puedan consumirlas.
Esta función de 280 líneas ejecuta 12 tareas aisladas: obtiene la configuración de entity-manager de forma asíncrona, pero en un solo hilo; busca drivers I2C a través de sysfs; comprueba la presencia de dispositivos en el bus; coteja la configuración con los dispositivos encontrados; crea, activa o recrea objetos HwmonTempSensor; los registra en D-Bus; gestiona los umbrales de activación; gestiona la frecuencia de sondeo; gestiona el estado de energía; implementa la lógica para varios sensores en un mismo dispositivo I2C; soporta un escaneo completo inicial al arrancar; y procesa actualizaciones puntuales cuando la configuración cambia sobre la marcha.
El anidamiento de bucles y condicionales llega a 6-7 niveles de profundidad (for → for/if → if → if → if). Cuando el desarrollador le pidió a la IA que analizara los problemas, el agente dio rápido con el diagnóstico: una función sobrecargada, lógica tipo espagueti que mezcla operaciones dispares (búsqueda, cotejo, creación, filtrado), y una imposibilidad total de testearla.
7 pasos concretos de refactorización
El agente propuso extraer una clase SensorScanner, donde cada paso se convierte en un método privado de 20-30 líneas:
Paso 1: crear un struct SensorScanner con un constructor que reciba los parámetros io, objectServer, sensors, dbusConnection, sensorsChanged y activateOnly; exponer un único método público scan como punto de entrada para procesar la configuración.
Paso 2: dividir la lógica en métodos privados: buildConfigMap (parseo de la configuración), findSensorPaths (búsqueda de rutas sysfs), ensureDevice (instanciación de dispositivos I2C), processPath (procesamiento de una ruta sysfs en el bucle), matchConfig (cotejo de una ruta con la configuración).
Paso 3: eliminar la duplicación en la creación de sensores. En el código original, la lógica de creación de HwmonTempSensor aparece duplicada dos veces, una para el sensor principal y otra para los adicionales. Moverla a un método createOrActivateSensor con parámetros como hwmonFile, i2cDev y otros. Esto ahorró 60 líneas de copia-pega.
Pasos 4-7: mover los identificadores Name1..N a estructuras de coincidencia separadas; separar la lógica del escaneo completo de la de actualización de configuración; aislar todos los efectos secundarios en métodos dedicados; sustituir el anidamiento profundo de ifs por continues tempranos.
La paradoja: la refactorización hizo el código más bonito, pero no más corto
Tras implementar las propuestas del agente, createSensors se redujo a una sola línea:
```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ```
El anidamiento desapareció, los métodos se volvieron pequeños, las estructuras quedaron explícitas. Es exactamente el tipo de código que piden en las entrevistas técnicas: C++20, plantillas, RAII, structured bindings.
Pero el git diff mostró algo inesperado: hubo notablemente más código en verde (líneas nuevas) que en rojo (líneas eliminadas). El volumen total de código creció, aunque el copia-pega fue localizado y eliminado. ¿La razón? Los 10 nuevos métodos requieren 10 firmas; hicieron falta nuevas estructuras y clases (para no pasar una decena de parámetros por referencia); y se necesitaron definiciones adicionales para aislar la lógica. El código espantoso simplemente se volvió estructurado a la moda.
La verdadera revelación: no se puede refactorizar una sola función de
forma aislada
Aquí es donde empieza el dolor de verdad. El horror de esta función concreta no es el error de un programador cansado ni un fallo casual. Es la consecuencia directa e inevitable de las decisiones arquitectónicas pobres de todo el proyecto.
OpenBMC presume de sus características clave: reconfigurable en tiempo de ejecución desde D-Bus; aislado -cada tipo de sensor corre en su propio daemon-; asíncrono y de un solo hilo -usa los bindings de sdbusplus/asio-. Suena moderno y escalable.
La realidad es otra. En lugar de un algoritmo simple (leer el JSON de configuración → encontrar los dispositivos → ponerse a trabajar), los autores dividieron un proceso indivisible por naturaleza entre varios microservicios. Los servicios individuales de sensores no leen directamente los archivos JSON de configuración; eso lo hace un servicio central, entity-manager. Este analiza TODAS las configuraciones y publica los datos en D-Bus... ¡por partes! Todos los microservicios se ven obligados a esperar hasta que se publique la parte de la configuración que necesitan.
El problema de fondo: no hay garantía de atomicidad. El servicio de sensores no tiene forma de saber cuándo entity-manager ha terminado de difundir los datos de todos los dispositivos del sistema. Quienes diseñaron D-Bus y el concepto de microservicios para este sistema embebido, al parecer, nunca oyeron hablar de las transacciones.
La solución: un timeout calculado a ojo. Mientras entity-manager envía fragmentos de configuración, un filtro reinicia constantemente un contador. Se fija un retardo de 1 segundo (filterTimer.expires_after(std::chrono::seconds(1))). Si pasa un segundo entero sin que lleguen datos nuevos, se asume que la configuración está completa y se invoca createSensors.
Es precisamente esta carencia arquitectónica la que convirtió a createSensors en un infierno. Tiene que lidiar sobre la marcha con las banderas firstScan, sensorsChanged y activateOnly, porque los datos llegan en fragmentos y el sistema debe recordar correctamente su estado y reaccionar a las actualizaciones.
La conclusión: esto es una tarea de Sísifo. No se puede refactorizar una sola función de C++, por muy terrorífica que sea, y esperar que eso salve el proyecto. El código aquí no es la causa raíz; es solo la implementación de unos conceptos abstractos que alguien metió en un sistema embebido sin pensar en los detalles.
Preguntas frecuentes sobre la noticia
¿Qué se sabe sobre Qué puede hacer realmente un agente de IA al refactorizar?
Un desarrollador analizó el microservicio HwmonTempSensor de OpenBMC, el proyecto de firmware abierto para los módulos de gestión de servidores basados en Linux. Todo el servicio gira en torno a una única función, createSensors, que recoge lecturas de sensores de hardware (temperatura, presión, humedad, corriente, voltaje) y las publica a través del bus D-Bus para que otros servicios, como la interfaz web, puedan consumirlas. Esta función de 280 líneas ejecuta 12 tareas aisladas: obtiene la configuración de entity-manager de forma asíncrona, pero en un solo hilo; busca driv
¿Qué se sabe sobre 7 pasos concretos de refactorización?
El agente propuso extraer una clase SensorScanner, donde cada paso se convierte en un método privado de 20-30 líneas: Paso 1: crear un struct SensorScanner con un constructor que reciba los parámetros io, objectServer, sensors, dbusConnection, sensorsChanged y activateOnly; exponer un único método público scan como punto de entrada para procesar la configuración. Paso 2: dividir la lógica en métodos privados: buildConfigMap (parseo de la configuración), findSensorPaths (búsqueda de rutas sysfs), ensureDevice (instanciación de dispositivos I2C), processPath (procesamiento de
¿Qué se sabe sobre La paradoja: la refactorización hizo el código más bonito, pero no más corto?
Tras implementar las propuestas del agente, createSensors se redujo a una sola línea: ```cpp void createSensors(...) { auto scanner = std::make_shared<SensorScanner>(...); scanner->run(); } ``` El anidamiento desapareció, los métodos se volvieron pequeños, las estructuras quedaron explícitas. Es exactamente el tipo de código que piden en las entrevistas técnicas: C++20, plantillas, RAII, structured bindings. Pero el git diff mostró algo inesperado: hubo notablemente más código en verde (líneas nuevas) que en rojo (líneas eliminadas). El volumen total de código creció, aunqu
Build & Ship Production AI Agents Without Boilerplate Code
Start your free 72-hour trial at Hamidun Academy (academy.hamidun.com) — interactive lessons on Claude Code & Cursor, live Tech Radar, and production agent templates.
Lo esencial de la IA — una vez por semana
Siete historias que de verdad importaron, elegidas a mano. Sin ruido ni notas de prensa.
¡Listo! Revisa tu correo para la confirmación.