“huelelos” al principio. En la práctica, los olores de código son señales de advertencia que sugieren problemas futuros. El código puede funcionar hoy, pero su estructura sugiere que será difícil de mantener, probar, escalar o proteger. Los olores no son necesariamente insectos; son indicadores de deuda de diseño y riesgo de producto a largo plazo.
Estos olores generalmente se manifiestan como una entrega más lenta y un mayor riesgo de cambio, regresiones e incidentes de producción más frecuentes y resultados de IA/ML menos confiables, a menudo impulsados por fugas, sesgos o derivas que socavan la evaluación y la generalización.
El camino del prototipo a la producción
La mayoría de las fases en el desarrollo de productos de datos/IA pueden variar, pero normalmente siguen un camino similar. Normalmente, comenzamos con un prototipo: primero se esboza una idea, seguida de una pequeña implementación para demostrar su valor. Se pueden utilizar herramientas como Streamlit, Gradio o n8n para presentar un concepto muy simple utilizando datos sintéticos. En estos casos, se evita el uso de datos reales sensibles y se reducen los problemas de privacidad y seguridad, especialmente en empresas grandes, sensibles a la privacidad o altamente reguladas.
Luego, pasa a la PoC, donde utiliza una muestra de datos reales y profundiza en las funciones mientras trabaja en estrecha colaboración con la empresa. Después de eso, avanza hacia la productización, creando un MVP que evoluciona a medida que valida y captura valor comercial.
La mayoría de las veces, los prototipos y las PoC se crean rápidamente, y la IA hace que su entrega sea aún más rápida. El problema es que este código rara vez cumple con los estándares de producción. Antes de que pueda ser robusto, escalable y seguro, generalmente necesita una refactorización en ingeniería (estructura, legibilidad, pruebas, mantenibilidad), seguridad (control de acceso, protección de datos, cumplimiento) y calidad de ML/AI (evaluación, monitoreo de deriva, reproducibilidad).
Olores típicos que ves… o no 🫥
Esta deuda técnica oculta (a menudo visible como el olor del código) es fácil de pasar por alto cuando los equipos buscan victorias rápidas, y la “codificación de vibraciones” puede amplificarla. Como resultado, puede encontrarse con problemas como:
Código duplicado: la misma lógica se copia en varios lugares, por lo que las correcciones y los cambios se vuelven lentos e inconsistentes con el tiempo. Guión divino/función divina: un archivo o función enorme lo hace todo, lo que hace que el sistema sea difícil de entender, probar, revisar y cambiar de forma segura porque todo está estrechamente acoplado. Esto viola el principio de responsabilidad única.[1]. En la era de los agentes, aparece el patrón del “agente divino”, donde un único punto de entrada de agente maneja el enrutamiento, la recuperación, las indicaciones, las acciones y el manejo de errores, todo en un solo lugar. Expansión de reglas: el comportamiento crece en largas cadenas if/elif para nuevos casos y excepciones, lo que obliga a realizar ediciones repetidas a la misma lógica central y aumentar las regresiones. Esto viola el principio abierto-cerrado (OCP): sigues modificando el núcleo en lugar de extenderlo.[1]. He visto esto en las primeras etapas del desarrollo de agentes, donde el enrutamiento de intenciones, el manejo de la etapa principal, las reglas específicas de cada país y las excepciones de casos especiales se acumulan rápidamente en largas cadenas condicionales. Valores codificados: rutas, umbrales, ID y detalles específicos del entorno están integrados en el código, por lo que los cambios requieren ediciones de código en varios lugares en lugar de simples actualizaciones de configuración. Estructura deficiente del proyecto (o diseño de carpetas): la lógica de la aplicación, la orquestación y la configuración de la plataforma conviven, lo que desdibuja los límites y dificulta la implementación y el escalado. Efectos secundarios ocultos: las funciones realizan un trabajo adicional que no espera (mutar el estado compartido, escribir archivos, actualizaciones en segundo plano), por lo que los resultados dependen del orden de ejecución y los errores se vuelven difíciles de rastrear. Falta de pruebas: no hay comprobaciones automáticas para detectar la desviación después de los cambios de código, aviso, configuración o dependencia, por lo que el comportamiento puede cambiar silenciosamente hasta que los sistemas fallen. (Lamentablemente, no todo el mundo se da cuenta de que las pruebas son baratas y los errores no). Nomenclatura y estructura inconsistentes: hace que el código sea más difícil de entender e incorporar a otros, ralentiza las revisiones y hace que el mantenimiento dependa del autor original. Reglas ocultas/sobrescritas: el comportamiento depende de entradas no probadas, sin versiones o administradas de manera flexible, como indicaciones, plantillas, configuraciones, etc. Como resultado, el comportamiento puede cambiar o sobrescribirse sin trazabilidad. Brechas de seguridad (protecciones faltantes): aspectos como la validación de entradas, los permisos, el manejo de secretos o los controles de PII a menudo se omiten en las primeras etapas. Lógica heredada enterrada: el código antiguo, como canalizaciones, ayudas, utilidades, etc., permanece disperso en la base del código mucho después de que el producto haya cambiado. Se vuelve más difícil confiar en el código porque codifica suposiciones obsoletas, lógica duplicada y caminos muertos que aún se ejecutan (o se pudren silenciosamente) en producción. Operaciones ciegas (sin alertas/sin detección): las fallas no se notan hasta que un usuario se queja, alguien verifica manualmente los registros de CloudWatch o se interrumpe un trabajo posterior. Es posible que existan registros, pero nadie monitorea activamente las señales importantes, por lo que los incidentes pueden pasar desapercibidos. Esto sucede a menudo cuando los sistemas externos cambian fuera del control del equipo o cuando muy pocas personas entienden el sistema o los datos. Integraciones con fugas: la lógica empresarial depende de detalles específicos de API/SDK (nombres de campo, parámetros requeridos, códigos de error), por lo que pequeños cambios de proveedor fuerzan correcciones dispersas en todo el código base en lugar de un solo cambio en un adaptador. Esto viola el principio de inversión de dependencia (DIP)[1]. Deriva del entorno (puesta en escena ≠ producción): los equipos tienen desarrollo/puesta en escena/pro, pero la puesta en escena no es realmente similar a la producción: diferentes configuraciones, permisos o dependencias, por lo que crea una confianza falsa: todo se ve bien antes del lanzamiento, pero los problemas reales solo aparecen en la producción (a menudo terminan en una reversión).
Y la lista sigue… y sigue.
El problema no es que los prototipos sean malos. El problema es la brecha entre la velocidad del prototipo y la responsabilidad de la producción, cuando los equipos, por una razón u otra, no invierten en las prácticas que hacen que los sistemas sean confiables, seguros y capaces de evolucionar.
También es útil ampliar la idea de “olores de código” a los olores de modelos y canalizaciones: señales de advertencia de que el sistema puede estar produciendo resultados confiables pero engañosos, incluso cuando las métricas agregadas parecen excelentes. Los ejemplos comunes incluyen brechas de equidad (las tasas de error de los subgrupos son consistentemente peores), derrames/fugas (la evaluación incluye accidentalmente información futura o relacional que no existirá en el momento de la decisión, lo que genera un desajuste entre desarrollo y producción).[7]), o/y multicolinealidad (características correlacionadas que hacen que los coeficientes y las explicaciones sean inestables). Estos no son casos extremos académicos; predicen de manera confiable fallas posteriores, como generalizaciones débiles, resultados injustos, interpretaciones poco confiables y caídas dolorosas de la producción.
Si cada desarrollador resuelve independientemente el mismo problema de forma diferente (sin un estándar compartido), es como tener varios controles remotos (cada uno con comportamiento diferente) para el mismo televisor. Los principios de la ingeniería de software siguen siendo importantes en la era de la codificación por vibración. Son los que hacen que el código sea confiable, fácil de mantener y seguro de usar como base para productos reales.
Ahora, la cuestión práctica es cómo reducir estos riesgos sin ralentizar a los equipos.
Por qué la IA acelera los olores del código
Los generadores de códigos de IA no saben automáticamente qué es lo más importante en su base de código. Generan resultados basados en patrones, no en su producto o contexto comercial. Sin restricciones y pruebas claras, puedes terminar con cinco minutos de “generación de código” seguidos de cien horas de depuración ☠️.
Si se usa descuidadamente, la IA puede incluso empeorar las cosas:
Simplifica demasiado o elimina partes importantes. Agrega ruido: código innecesario o duplicado y comentarios detallados. Pierde contexto en bases de código grandes (comportamiento perdido en el medio)
Un artículo reciente del MIT Sloan señala que la IA generativa puede acelerar la codificación, pero también puede hacer que los sistemas sean más difíciles de escalar y mejorar con el tiempo cuando los prototipos rápidos se integran silenciosamente en los sistemas de producción.[4].
De cualquier manera, los refactorizadores no son baratos, ya sea que el código haya sido escrito por humanos o producido por una IA mal utilizada, y el costo generalmente se manifiesta más tarde en una entrega más lenta, un mantenimiento doloroso y una lucha contra incendios constante. En mi experiencia, ambos suelen compartir la misma causa fundamental: fundamentos débiles de la ingeniería de software.
Algunos de los peores olores no son nada técnicos; son organizativos. Los equipos pueden tener deudas menores 😪 porque no duelen de inmediato, pero el costo oculto aparece más tarde: la propiedad y los estándares no escalan. Cuando los autores originales se van, son promovidos o simplemente siguen adelante, el código mal estructurado se entrega a otra persona sin convenciones compartidas de legibilidad, modularidad, pruebas o documentación. El resultado es predecible: el mantenimiento se convierte en arqueología, la entrega se ralentiza, el riesgo aumenta y la persona que hereda el sistema a menudo también hereda la culpa.
Listas de verificación: una lista resumida de recomendaciones
Este es un tema complejo que se beneficia del criterio de los ingenieros superiores. Una lista de verificación no reemplazará la ingeniería de plataformas, la seguridad de las aplicaciones o los revisores experimentados, pero puede reducir el riesgo al hacer que los conceptos básicos sean consistentes y más difíciles de omitir.
1. La pieza que falta: diseño de “primero el problema”
Una mentalidad de “diseño primero/problema primero” significa que antes de construir un producto de datos o un sistema de inteligencia artificial (o acumular continuamente características en indicaciones o reglas si/si no), se define claramente el problema, las limitaciones y los modos de falla. Y no se trata sólo de diseño de producto (qué se construye y por qué), sino también de diseño de software (cómo se construye y cómo evoluciona). Esa combinación es difícil de superar.
También es importante recordar que los equipos de tecnología (ingenieros de IA/ML, científicos de datos, control de calidad, ciberseguridad y profesionales de plataformas) son parte del negocio, no una entidad separada. Con demasiada frecuencia, los roles altamente técnicos se consideran desconectados de preocupaciones comerciales más amplias. Esto sigue siendo un desafío para algunos líderes empresariales, quienes pueden considerar a los expertos técnicos como sabelotodos en lugar de profesionales (no siempre es cierto).[2].
2. Barandillas de código: controles de deriva de calidad, seguridad y comportamiento
En la práctica, la deuda técnica crece cuando la calidad depende de que la gente “recuerde” los estándares. Las listas de verificación hacen que las expectativas sean explícitas, repetibles y escalables entre los equipos, pero las barreras de seguridad automatizadas van más allá: no se puede fusionar código en producción a menos que los conceptos básicos sean ciertos. Esto garantiza una base mínima de calidad y seguridad en cada cambio.
Las comprobaciones automatizadas ayudan a evitar que los problemas más comunes de los prototipos lleguen a producción. En la era de la IA, donde el código se puede generar más rápido de lo que se puede revisar, las barreras de seguridad del código actúan como un cinturón de seguridad al hacer cumplir los estándares de manera consistente. Una forma práctica es realizar comprobaciones lo antes posible, no sólo en CI. Por ejemplo, los ganchos de Git, especialmente los ganchos de confirmación previa, pueden ejecutar validaciones incluso antes de que se confirme el código.[5]. Luego, las canalizaciones de CI ejecutan el conjunto completo en cada solicitud de extracción, y las reglas de protección de sucursales pueden exigir que esas comprobaciones se aprueben antes de que se permita una fusión, lo que garantiza que se cumpla la calidad del código incluso cuando se omitan los estándares.
Una línea de base sólida generalmente incluye:
Linters (p. ej., ruff): impone un estilo coherente y detecta problemas comunes (importaciones no utilizadas, nombres no definidos, patrones sospechosos). Pruebas (p. ej., pytest): evita cambios de comportamiento silenciosos al verificar que las funciones clave y las canalizaciones aún se comportan como se espera después de las ediciones de código o configuración. Escaneo de secretos (por ejemplo, Gitleaks): bloquea confirmaciones accidentales de tokens, contraseñas y claves API (a menudo codificadas en prototipos). Escaneo de dependencias (por ejemplo, Dependabot/OSV): señala paquetes vulnerables con anticipación, especialmente cuando los prototipos incorporan bibliotecas rápidamente. Evaluaciones de LLM (p. ej., regresión de indicaciones): si las indicaciones y la configuración del modelo afectan el comportamiento, trátelas como código probando las entradas y las salidas esperadas para captar la deriva.[6].
Esta es la lista corta, pero los equipos a menudo agregan barreras de seguridad adicionales a medida que los sistemas maduran, como verificación de tipos para detectar errores de interfaz y "Ninguno" temprano, análisis de seguridad estático para señalar patrones riesgosos, límites de cobertura y complejidad para evitar código no probado y pruebas de integración para detectar cambios importantes entre servicios. Muchos también incluyen escaneo de infraestructura como código y de imágenes de contenedores para detectar configuraciones inseguras en la nube, además de calidad de datos y monitoreo de modelo/LLM para detectar cambios en esquemas y comportamientos, entre otros.
¿Cómo ayuda esto?
El código generado por IA a menudo incluye textos repetitivos, sobras y atajos arriesgados. Las barreras de seguridad como los linters (por ejemplo, Ruff) detectan problemas predecibles rápidamente: importaciones desordenadas, código inactivo, diferencias ruidosas, patrones de excepción riesgosos y atajos comunes de Python. Las herramientas de escaneo ayudan a prevenir filtraciones secretas accidentales y dependencias vulnerables, y las pruebas y evaluaciones hacen visibles los cambios de comportamiento mediante la ejecución de conjuntos de pruebas y solicitan regresiones en cada solicitud de extracción antes de la producción. El resultado es una iteración más rápida con menos sorpresas en la producción.
Liberar barandillas
Más allá de las comprobaciones de solicitud de incorporación a producción (PR), los equipos también utilizan un entorno de prueba como barrera de protección del ciclo de vida: una configuración similar a la producción con datos controlados para validar el comportamiento, las integraciones y los costos antes del lanzamiento.
3. Barandillas humanas: estándares compartidos y explicabilidad
Las buenas prácticas de ingeniería, como revisiones de código, programación en pares, documentación y estándares de equipo compartidos, reducen los riesgos del código generado por IA. Un modo de falla común en la codificación vibe es que el autor no puede explicar claramente qué hace el código, cómo funciona o por qué debería funcionar. En la era de la IA, es esencial articular la intención y el valor en un lenguaje sencillo y documentar las decisiones de manera concisa, en lugar de depender de resultados detallados de la IA. No se trata de memorizar la sintaxis; se trata de diseño, buenas prácticas y una disciplina de aprendizaje compartido, porque la única constante es el cambio.
4. IA responsable por diseño
Las barreras de seguridad no son solo estilo de código y comprobaciones de CI. Para los sistemas de IA, también se necesitan barreras de seguridad a lo largo de todo el ciclo de vida, especialmente cuando un prototipo se convierte en un producto real. Un enfoque práctico es una lista de verificación de “IA responsable desde el diseño” que cubra controles mínimos desde la preparación de datos hasta la implementación y la gobernanza.
Como mínimo, debe incluir:
Preparación de datos: protección de la privacidad, controles de calidad de los datos, controles de parcialidad/imparcialidad. Desarrollo de modelos: alineación empresarial, explicabilidad, pruebas de robustez. Seguimiento y control de versiones de experimentos: reproducibilidad a través del control de versiones de conjuntos de datos, códigos y modelos. Evaluación del modelo: pruebas de estrés, análisis de subgrupos, estimación de la incertidumbre cuando sea relevante. Implementación y monitoreo: monitoree la deriva/latencia/confiabilidad por separado de los KPI comerciales; definir alertas y reglas de reentrenamiento. Gobernanza y documentación: registros de auditoría, propiedad clara y documentación estandarizada para aprobaciones, análisis de riesgos y trazabilidad.
La página de la figura 1 es sólo un primer paso. Úselo como base, luego adáptelo y amplíelo con su experiencia y el contexto de su equipo.
La página de la figura 1 es sólo un primer paso. Úselo como base, luego adáptelo y amplíelo con su experiencia y el contexto de su equipo.
5. Pruebas adversarias
Existe una extensa literatura sobre aportes contradictorios. En la práctica, los equipos pueden probar la solidez introduciendo entradas (en LLM y ML clásico) que el sistema nunca encontró durante el desarrollo (cargas útiles con formato incorrecto, patrones similares a inyecciones, longitudes extremas, codificaciones extrañas, casos extremos). La clave es cultural: las pruebas contradictorias deben tratarse como una parte normal del desarrollo y la seguridad de las aplicaciones, no como un ejercicio único.
Esto enfatiza que la evaluación no es un evento único fuera de línea: los equipos deben validar los modelos a través de procesos de lanzamiento por etapas y mantener continuamente conjuntos de datos de evaluación, métricas y verificaciones de subgrupos para detectar fallas tempranas y reducir el riesgo antes de la implementación completa.[8].
Conclusión
Un prototipo suele parecer pequeño: un cuaderno, un guión, una aplicación de demostración. Pero una vez que toca datos reales, usuarios reales e infraestructura real, se convierte en parte de un gráfico de dependencia, una red de componentes donde pequeños cambios pueden tener un radio de explosión sorprendente.
Esto es importante en los sistemas de IA porque el ciclo de vida involucra muchas partes móviles interdependientes, y los equipos rara vez tienen visibilidad completa de ellas, especialmente si no lo planifican desde el principio. Esa falta de visibilidad hace que sea más difícil anticipar los impactos, particularmente cuando se trata de datos, modelos o servicios de terceros.
Lo que esto incluye a menudo:
Dependencias de software: bibliotecas, contenedores, pasos de compilación, imágenes base, ejecutores de CI. Dependencias de tiempo de ejecución: servicios posteriores, colas, bases de datos, almacenes de características, puntos finales de modelo. Dependencias específicas de IA: fuentes de datos, incrustaciones/almacenamiento de vectores, indicaciones/plantillas, versiones de modelos, ajustes finos, bases de conocimiento RAG. Dependencias de seguridad: IAM/permisos, gestión de secretos, controles de red, gestión de claves y políticas de acceso. Dependencias de gobernanza: requisitos de cumplimiento, auditabilidad y procesos claros de propiedad y aprobación.
Para las empresas, esto no siempre es obvio. Un prototipo puede parecer “terminado” porque se ejecuta una vez y produce un resultado, pero los sistemas de producción se comportan más como seres vivos: interactúan con los usuarios, los datos, los proveedores y la infraestructura, y necesitan un mantenimiento continuo para seguir siendo confiables y útiles. Es fácil subestimar la complejidad de la evolución de estos sistemas porque gran parte de ella es invisible hasta que algo se rompe.
Aquí es donde las victorias rápidas pueden resultar engañosas. La velocidad puede ocultar el acoplamiento, la falta de barreras de seguridad y las brechas operativas que sólo se manifiestan más tarde como incidentes, regresiones y costosas reelaboraciones. Este artículo inevitablemente no llega a cubrir todo, pero el objetivo es hacer que esa complejidad oculta sea más visible y fomentar una mentalidad de diseño primero que vaya más allá de la demostración.
Referencias
[1]Martín, RC (2008). Código limpio: un manual de artesanía en software ágil. Prentice Hall.
[2]Hunt, A. y Thomas, D. (1999). El programador pragmático: de oficial a maestro. Addison-Wesley.
[3]Kanat-Alexander, M. (2012). Simplicidad de código: los fundamentos del software. Medios O'Reilly.
[4]Anderson, E., Parker, G. y Tan, B. (18 de agosto de 2025). Los costos ocultos de codificar con IA generativa (Reimpresión 67110). Revisión de la gestión de préstamos del MIT.
[5]iosutrón. (2023, 23 de marzo). ¡¡Construye un mejor código!!. Perdido en tecnología. WordPress.
[6]Arize AI. (Dakota del Norte). La guía definitiva para la evaluación de LLM: una guía práctica para crear e implementar estrategias de evaluación para aplicaciones de IA. Obtenido el 10 de enero de 2026 de Arize AI.
[7]Gomes-Gonçalves, E. (2025, 15 de septiembre). Sin mirar hacia adelante: detección de fraude gráfico consciente del tiempo. Hacia la ciencia de datos. Obtenido el 11 de enero de 2026 de Towards Data Science.
[8]Shankar, S., García, R., Hellerstein, JM y Parameswaran, AG (2022, 16 de septiembre). Operacionalización del aprendizaje automático: un estudio de entrevista. arXiv:2209.09125. Obtenido el 11 de enero de 2026 de arXiv.