Cuando la IA escribe tu aplicación React pero omite los principios de código limpio
Aprende siete hábitos de código limpio —DRY, responsabilidad única, cláusulas de protección y más— que el código React generado por IA suele violar y cómo corregirlos.
Hace poco un cliente nos entregó un proyecto que consistía en crear un sitio web para pedir pizzas.
Al principio, el alcance del proyecto parecía manejable.
Una página de aterrizaje.
Una sección de menú.
Diferentes categorías de pizzas.
Páginas individuales para cada producto.
Un carrito de compras.
Un flujo de pago.
Además, un panel de administración para gestionar todo en la parte posterior.
La suposición natural era:
"Esto debería ser rápido."
Luego surgió la decisión de recurrir a un asistente de programación basado en IA.
Ahí es donde las cosas se volvieron interesantes.
Dejar que la IA se encargue de la mayor parte de la programación
El proyecto se desarrolló con React.
En lugar de escribir cada línea manualmente, se enviaban instrucciones a la IA para que generara cada parte.
Una instrucción como:
"Crea el componente de la tarjeta de pizzas."
generaba el componente al instante.
Siguiente paso:
"Construir el carrito de compras."
Manejado.
Luego:
"Construir la pantalla de pago."
También manejado.
Luego:
"Crear un panel de control para administradores que muestre estadísticas, pedidos e información de clientes."
Entregado.
La velocidad fue realmente impresionante.
Trabajos que normalmente tomarían horas se completaban en minutos.
El resultado incluyó:
- Componentes de React
- Formularios
- Tablas de datos
- Widgets para el panel de control
- Integración con API
- Indicadores de carga
- Gestión de errores
- Diseños que se adaptaban al tamaño de la pantalla
Y la aplicación realmente funcionaba.
Todo parecía prometedor.
Parecía que se había encontrado el flujo de trabajo ideal:
Idea → Prompt → Código → Lanzamiento
Pero faltaba un paso crucial.
Revisar lo que se había creado.
El cliente nunca vio el código en sí
Desde el punto de vista del cliente, todo parecía genial.
El sitio funcionaba sin problemas.
La pantalla de control operaba correctamente.
El menú se mostraba como era debido.
Los pedidos aparecían tal como se esperaba.
Visualmente, estaba impecable.
Entonces, ¿dónde estaba el problema?
El problema no era visible desde afuera.
Dentro de las entrañas del sistema, la base de código se estaba convirtiendo silenciosamente en un problema de mantenimiento.
No era obvio de inmediato.
Pero a medida que se añadían más funciones, comenzó a aparecer un patrón.
Siempre surgía el mismo pensamiento:
"Espera... ¿no se ha creado ya algo casi idéntico a esto?"
Fue entonces cuando surgió una importante conclusión:
La IA es realmente hábil para generar código funcional.
Pero el código limpio no es algo que genere automáticamente.
Problema n.º 1: Lógica repetida por todas partes
Resultó que una lógica casi idéntica estaba dispersa por toda la aplicación.
Por ejemplo, varias funciones separadas calculaban los totales de forma independiente.
Una de ellas era así:
const total = price * quantity;
En otro lugar, apareció una versión casi idéntica:
const orderTotal = price * quantity;
Y en otro archivo más:
const cartAmount = price * quantity;
Todas las tres realizaban esencialmente el mismo cálculo.
Técnicamente no había nada roto.
Pero en cuanto era necesario cambiar las reglas de precios, había que localizar y actualizar cada una de las copias.
Es en ese momento cuando se hizo evidente el primer principio del código limpio.
1. DRY: No te repitas
Cada vez que la misma lógica aparece repetidamente, la pregunta importante es:
¿Se podría consolidar en una sola fuente?
Por ejemplo:
const calculateItemTotal = (price, quantity) => {
return price * quantity;
};
A partir de ese momento, cada parte de la aplicación puede llamar a la misma función compartida.
El objetivo no es forzar la reutilización en todas partes.
Simplemente se trata de eliminar la duplicación que no sirve para nada.
Problema n.º 2: Un componente hacía todo
En algún momento, se abrió uno de los archivos generados por React para examinarlo más de cerca.
Siguió creciendo.
Línea tras línea.
Simplemente seguía expandiéndose.
Ese único componente era responsable de:
- Solicitudes a la API
- Gestión del estado de los formularios
- Lógica de validación
- Visibilidad de los modales
- Renderizado de una tabla
En resumen, un solo archivo era, en esencia, responsable de ejecutar la mitad de la aplicación por sí mismo.
La conclusión obvia fue:
"Mantener esto será complicado."
Por lo tanto, el componente se dividió en partes más pequeñas.
En lugar de mantener esta estructura:
Orders.jsx
↓
800+ lines
se reestructuró de manera similar a:
Orders
├── OrderFilters
├── OrderTable
├── OrderRow
├── OrderModal
└── useOrders
Ese cambio condujo directamente al siguiente principio.
2. Responsabilidad única
Un componente o función debe tener una tarea clara.
Si no puedes describir qué hace un componente en una sola oración, es muy probable que esté asumiendo demasiadas funciones.
Por ejemplo:
OrderTablemuestra una lista de pedidos.
Esa es una descripción clara y concisa.
Compárela con esto:
OrderTablerecupera los pedidos, verifica los permisos del usuario, calcula los totales, dispara notificaciones y muestra todo en una tabla.
Ese tipo de descripción es una señal de alerta.
Problema n.º 3: Las sentencias If anidadas dominaron el código
En un momento dado, parte de la lógica del proyecto se veía así:
if (user) {
if (user.isActive) {
if (user.isVerified) {
// create order
}
}
}
Funcionaba correctamente.
Pero revisarlo resultaba agotador.
Por eso se reescribió de la siguiente manera:
if (!user) return;
if (!user.isActive) return;
if (!user.isVerified) return;
// create order
Esa versión era mucho más fácil de entender a simple vista.
Aquí es donde entra en juego el siguiente hábito:
3. Devoluciones anticipadas / cláusulas de protección
En lugar de ocultar la lógica principal bajo varias capas de condiciones anidadas, es útil manejar los casos inválidos desde el principio y salir temprano del proceso.
Invalid?
↓
Return
Invalid?
↓
ReturnEverything okay?
↓
Do the actual work
El resultado es que la lógica principal se mantiene simple, legible y sin sangrías innecesarias.
Problema n.º 4: La IA sugirió nuevos componentes para cosas que ya existían
Ese error fue un poco vergonzoso.
La instrucción dada a la IA fue:
"Crea un modal de confirmación para eliminar un pedido."
La IA respondió generando un componente completamente nuevo.
Casi fue aceptado sin cuestionamientos.
Luego, una búsqueda rápida en el proyecto reveló algo importante.
Ya existía un componente modal.
Simplemente se podría haber reutilizado.
Ese momento dejó claro algo:
La IA no sabe automáticamente qué ya existe en una base de código dada.
Incluso cuando lo sabe, aún puede optar por crear algo nuevo en lugar de reutilizar lo que ya existe.
Un enfoque mejor es plantear la pregunta de la siguiente manera:
"Antes de crear un nuevo componente, revisa la base de código existente en busca de elementos que puedan ser reutilizados."
Esa experiencia apunta a otra regla:
4. Reutiliza antes de crear
Antes de agregar:
- un nuevo componente
- una nueva función utilitaria
- un nuevo gancho personalizado
- un nuevo ayudante de API
pregunta:
"¿Ya existe esto en el proyecto?"
Una base de código limpia no se define por tener docenas de elementos reutilizables dispersos.
Se define por desarrolladores que reutilizan lo que ya está disponible en lugar de duplicarlo en silencio.
Problema #5: Los nombres de las variables a veces eran terribles
El código generado por IA llega rápidamente.
Y es fácil aceptar nombres de variables como estos:
const data = ...
const result = ...
const x = ...
const temp = ...
Estos nombres se compilan sin problemas y no causan errores.
Pero meses después, ¿a qué se refiere realmente data?
¿Son datos relacionados con pizza?
¿Información de pedidos?
¿Registros de clientes?
¿Algo relacionado con un panel de control?
Por lo tanto, el nombrado debe volverse más intencional a medida que crece el proyecto.
En lugar de escribir:
const data = await getOrders();
es preferible una versión más clara:
const orders = await getOrders();
Y en lugar de:
const x = users.filter(...);
esto se lee mucho mejor:
const activeUsers = users.filter(...);
Eso genera otra regla sencilla:
5. Utilice nombres que expliquen el código
Un nombrado descriptivo a menudo puede eliminar por completo la necesidad de comentarios.
Al leer algo como:
const activeUsers = users.filter(
(user) => user.isActive
);
Ya le indica con exactitud qué está sucediendo.
No hay necesidad de añadir:
// Filter users who are currently active
El código habla por sí mismo.
Problema #6: Optimizar cosas que no necesitaban optimización
En algún momento de un proyecto como este, es fácil empezar a pensar:
"Este código necesita ser más optimizado."
Eso a menudo conduce a investigar cosas como:
useMemo()
useCallback()
y algunos otros trucos de rendimiento.
Pero vale la pena detenerse aquí.
La optimización no es automáticamente algo bueno.
Tomemos un cálculo trivial como:
const total = price * quantity;
No hay razón para envolverlo en algún patrón de optimización elaborado solo porque existen las herramientas.
Hacerlo solo haría que el código sea más difícil de seguir.
Lo cual nos lleva a otra lección:
6. Optimice solo cuando haya un problema real
No intente optimizar solo porque:
"Alguien en línea dijo que esta técnica es más rápida."
En lugar de eso, localice primero el cuello de botella real.
¿Un componente se vuelve a renderizar con demasiada frecuencia?
¿Una llamada a la API es lenta?
¿Un cálculo es realmente costoso?
¿El tamaño del paquete es demasiado grande?
¿Una consulta a la base de datos es ineficiente?
Identifique el problema real antes de tocar nada.
Solo entonces debería realizarse la optimización.
Código limpio más optimización innecesaria equivalen a complejidad innecesaria.
Problema n.º 7: Dejar de pedirle a la IA que "arregle todo"
Esta podría ser la lección más importante de un proyecto como este.
En algún momento, la tentación es simplemente decir:
"Simplemente limpia todo el proyecto por mí."
Pero vale la pena contenerse de hacer eso.
¿Qué significa exactamente "limpiar" en ese contexto?
La IA podría responder con:
- renombrar variables y archivos
- reorganizar la estructura de carpetas
- introducir nuevas abstracciones
- fusionar funciones entre sí
- eliminar código que considere innecesario
- reestructurar la arquitectura
Algunos de esos cambios podrían ser realmente útiles.
Otros podrían ser inútiles.
Y algunos podrían introducir errores silenciosamente.
Un enfoque más lento y gradual funciona mejor.
Comience preguntando:
">Mira este proyecto, pero aún no cambies nada."
Luego:
">Indica cualquier lógica duplicada."
Luego:
"Dígame cuáles de estas duplicaciones realmente merecen ser corregidas."
Solo después de eso se deben revisar las sugerencias.
Luego se aplica un cambio.
A continuación, se prueba.
Eso se convierte en la regla número siete:
7. Entienda antes de refactorizar
Nunca permita que la IA toque código que no se haya comprendido por completo primero.
Comience analizando.
Asegúrese de que la lógica esté clara.
Luego decida qué hacer.
Realice el cambio.
Verifíquelo con una prueba.
Lo que ilustra un sitio web de pizzas
Curiosamente, los clientes rara vez preguntan:
"¿Su código está limpio?"
La pregunta suele ser más simple:
"¿El sitio funciona?"
Y con frecuencia sí funciona.
Aun así, los desarrolladores tienen una responsabilidad que va más allá de esa primera versión funcional.
El software rara vez permanece estático una vez que se lanza.
Con el tiempo, un cliente vuelve con:
"¿Podemos agregar seguimiento de entregas?"
Luego:
"Añadamos códigos de descuento."
Luego:
"Necesitamos soporte para múltiples sucursales."
Luego:
"Añadamos cuentas para el personal del restaurante."
Luego:
"Queremos algunos informes."
En poco tiempo, ese pequeño sitio de pizzas se ha convertido en un sistema completo.
Ese es exactamente el punto en el que el código limpio comienza a justificar el esfuerzo.
La IA no fue la culpable
Vale la pena ser justos aquí.
La IA no es la responsable.
Es realmente valioso en un proceso de desarrollo como este.
Ayuda con:
- trabajar más rápido
- probar diferentes enfoques
- manejar tareas de programación repetitivas
- localizar errores
- crear estructuras básicas para componentes
- razonar sobre soluciones
El verdadero problema nunca es que la IA genere código.
Sino que el código generado a veces se acepta sin un examen suficientemente detallado.
Esos son dos problemas muy diferentes.
Un flujo de trabajo revisado para programar con IA
Después de un proyecto como este, tiene sentido cambiar el enfoque.
El proceso puede verse más o menos así:
Understand the requirement
↓
Explore existing code
↓
Plan the solution
↓
Ask AI for implementation
↓
Review the generated code
↓
Simplify
↓
Test
↓
Refactor if necessary
La IA sigue encargándose de una gran parte de la escritura real del código.
Pero más del proceso de razonamiento vuelve a recaer en el desarrollador.
Ese equilibrio parece ser hacia donde se dirige el desarrollo de software en su conjunto.
La IA escribe código, pero la base de código sigue siendo tuya
Ese es el mensaje principal de toda esta experiencia.
Una vez que la IA comienza a generar tu código, es tentador asumir:
"La IA escribió esto, así que debe entender lo que está haciendo."
Esa suposición no es válida.
La propiedad de la base de código sigue siendo tuya.
Tú eres quien la mantendrá en funcionamiento de ahora en adelante.
Tú eres quien tendrá que localizar los errores cuando aparezcan.
Tú eres quien necesitará explicar cómo funciona a otra persona.
Tú serás quien vuelva a revisarlo meses después y necesite comprenderlo de nuevo.
En algún momento, otro desarrollador podría abrir el proyecto y preguntarse:
"¿Cuál fue la lógica detrás de este enfoque?"
Lo ideal es que el propio código haga claro ese razonamiento.
Siete hábitos de código limpio que vale la pena mantener
Resumiendo toda esta experiencia, esto es lo que más me quedó grabado:
1. DRY
Evite duplicar la misma lógica en varios lugares sin una buena razón.
2. Una sola responsabilidad
Asegúrese de que cada función o componente tenga un propósito bien definido.
3. Devoluciones tempranas
Reduzca la anidación para que el camino principal de la lógica siga siendo fácil de seguir.
4. Reutilice antes de crear
Búsque soluciones existentes en el código antes de desarrollar algo nuevo.
5. Buenas denominaciones
Elija nombres que transmitan claramente qué representa una variable o función.
6. Optimice con pruebas
Evite agregar complejidad a menos que haya un problema de rendimiento medible que lo justifique.
7. Entienda antes de refactorizar
La IA puede proponer modificaciones, pero la decisión de cuáles merecen implementarse es suya.
Pensamientos finales
Al comenzar el proyecto del sitio de pizzas, supuse que la principal ventaja de la asistencia de la IA sería:
velocidad bruta.
Mirando hacia atrás, he llegado a creer que existe una ventaja aún mayor.
La IA libera capacidad mental para los aspectos del desarrollo que realmente requieren juicio.
En lugar de invertir esfuerzo en tareas repetitivas, podemos dirigir esa energía hacia mejores preguntas:
¿Es este realmente el enfoque correcto?
¿Se podría simplificar esto?
¿Ya existe algo similar en el proyecto?
¿Seguirá siendo válido este diseño cuando llegue la próxima función?
¿Algún otro desarrollador podría seguir este proceso?
Esa es la verdadera dificultad al trabajar con el desarrollo asistido por IA.
La IA es capaz de generar cientos de líneas de código casi al instante.
Confirmar que esas líneas realmente merezcan su lugar en la base de código sigue siendo nuestra responsabilidad.
Tal vez esa sea la definición actualizada de código limpio ahora que la IA forma parte del proceso:
Hacer que el código funcione no es la meta final; lo importante es poder mantenerlo posteriormente.
Lecturas relacionadas
- Por qué el acceso a la IA, y no su capacidad, es tu verdadero riesgo de dependencia — Este artículo analiza incidentes recientes relacionados con controles de exportación en Claude y GPT-5.6 para argumentar que el acceso a los modelos de IA es una variable volátil independiente de la capacidad bruta.