12k
All articles

Los fundamentos que sigues necesitando cuando programas con agentes

Revisa código generado por IA con criterio. Domina los fundamentos web de HTTP, el navegador, el estado, la seguridad, la depuración, Git y la arquitectura.

OpenReplay Team
OpenReplay Team
Los fundamentos que sigues necesitando cuando programas con agentes

Necesitas suficientes fundamentos de programación para evaluar el código que escribe un agente, no para teclearlo, porque un código que funciona puede estar mal diseñado, ser inseguro o lento.

La situación habitual: el agente dice que ha terminado, la página carga, los tests pasan y, aun así, no sabrías explicar el diff si un revisor te preguntara por él. Esa duda es razonable. Por lo general, apunta a una laguna concreta en lo que sabes.

Este artículo no trata sobre prompting ni sobre la configuración de herramientas. Para eso, consulta cómo obtener los mejores resultados de las herramientas de programación con IA y trucos y consejos para programar con IA. En su lugar, toma siete fundamentos de la web y asocia cada uno con un error realista de un agente y con el conocimiento que necesitas para detectarlo.

Puntos clave

  • El fetch() del navegador solo rechaza la promesa cuando falla la propia petición, por ejemplo por un error de red, así que el código que nunca comprueba response.ok trata un 404 o un 500 como un éxito.
  • Cualquier valor incluido en el bundle de JavaScript del frontend es público, incluidas las variables de entorno que el framework expone al cliente.
  • Ocultar un botón no es autorización: el servidor tiene que comprobar los permisos en cada petición.
  • git add -p te permite aceptar los cambios de un agente hunk por hunk, y git revert deshace un commit sin reescribir el historial.
FundamentoError típico del agenteQué lo detecta
HTTP200 con un cuerpo de errorSemántica de los códigos de estado
NavegadorListener sin limpiezaCiclo de vida de los efectos
EstadoDatos del servidor copiados en un storeFuente única de verdad
SeguridadSecreto en el bundle del clienteSaber qué se envía al navegador
DepuraciónError silenciadoStack traces, DevTools
GitAceptarlo todo de golpeStaging por hunks, revert
ArquitecturaNueva dependencia para una sola líneaAPIs de la plataforma

¿Por qué siguen importando los códigos de estado HTTP y la caché?

Los códigos de estado y las cabeceras de caché son la forma en que los clientes, los proxies y las herramientas de monitorización saben qué le ha pasado a una petición. Un endpoint que devuelve 200 OK con un mensaje de error en el cuerpo le dice a cada cliente, caché y herramienta de monitorización que la petición se completó con éxito. La RFC 9110 define los 2xx como éxito, así que ni el seguimiento de errores ni los reintentos se activarán. Ten a mano la referencia de códigos de estado HTTP cuando revises handlers.

En el lado del cliente hay una trampa equivalente. fetch() no rechaza la promesa ante códigos de estado HTTP de error, solo cuando falla la propia petición, así que tienes que comprobar Response.ok por tu cuenta:

const res = await fetch('/api/save', { method: 'POST', body });
if (!res.ok) throw new Error(`Save failed: ${res.status}`);

Los agentes también tienden a hacer polling de datos que rara vez cambian. Para configuraciones o listas de referencia, Cache-Control (definida en la RFC 9111) con revalidación mediante ETag (que devuelve 304 Not Modified, definido en la RFC 9110) elimina las peticiones redundantes. Los datos que realmente cambian en tiempo real son un problema aparte.

¿Cómo renderiza el navegador y cómo gestiona los eventos?

Los event listeners y las escrituras en el DOM tienen ciclos de vida, y el navegador te pasa factura si los ignoras. Un error habitual de los agentes es añadir un listener dentro de un efecto que no tiene función de limpieza ni array de dependencias. React ejecuta ese efecto después de cada renderizado, así que cada renderizado añade otro listener de resize. La solución devuelve una función de limpieza y declara sus dependencias:

useEffect(() => {
  window.addEventListener('resize', onResize);
  return () => window.removeEventListener('resize', onResize);
}, [onResize]);

La función de limpieza devuelta se ejecuta antes de la siguiente ejecución del efecto y al desmontar el componente. El segundo patrón del navegador que conviene detectar es el layout thrashing: un bucle que escribe un estilo y luego lee offsetHeight, una y otra vez. Cada lectura posterior a una escritura puede forzar un cálculo de layout síncrono. La solución es hacer primero todas las lecturas y después todas las escrituras.

¿Dónde debe residir el estado y cómo deben fluir los datos?

Cada dato debe tener un único propietario. Copiar datos del servidor en un store del lado del cliente crea una segunda fuente de verdad, y ambas copias se desincronizan en cuanto una cambia sin la otra. Una versión típica de un agente obtiene una lista, la guarda en useState o en un store de Zustand y la edita localmente mientras un refetch actualiza otra copia distinta.

La guía de React sobre la estructura del estado advierte contra la duplicación del estado. Para los datos del servidor, una capa de caché como TanStack Query o SWR es la propietaria de los datos y gestiona la invalidación. Para cualquier cosa que puedas calcular a partir del estado existente, deriva el valor durante el renderizado.

Fundamentos de seguridad: autenticación, XSS, secretos y entradas no confiables

La seguridad en el frontend depende de saber qué código se ejecuta en el navegador y cuál en el servidor. Cualquier valor incluido en el bundle de JavaScript del frontend es público. En Next.js, las variables de entorno son exclusivas del servidor por defecto. Añadir el prefijo NEXT_PUBLIC_ incrusta el valor de una variable en el bundle del cliente en tiempo de compilación, así que un agente que pone ese prefijo a una API key ha publicado la clave.

Ocultar un botón en la interfaz no es autorización. Cualquiera puede llamar directamente a la API, así que el servidor tiene que comprobar los permisos en cada petición. Este fallo se clasifica en OWASP Top 10:2025, A01 Broken Access Control.

Asignar una entrada del usuario a innerHTML hace que el navegador la interprete como marcado, y eso incluye atributos como onerror que ejecutan JavaScript. textContent inserta esa misma entrada como texto plano. JSX escapa por defecto las cadenas interpoladas, y dangerouslySetInnerHTML desactiva esa protección. La OWASP XSS Prevention Cheat Sheet cubre el resto.

¿Cómo depuras código que no has escrito tú?

Depurar significa encontrar la causa de un bug, no hacer desaparecer el síntoma. Los agentes a menudo «arreglan» un error envolviéndolo en un try/catch que lo silencia, o añadiendo optional chaining que oculta un valor undefined en lugar de rastrear de dónde viene. El error desaparece de la consola, pero el bug sigue ahí.

Antes de aceptar un arreglo, lee el stack trace, reproduce el bug y revisa el panel Network de Chrome DevTools para ver qué contenían realmente la petición y la respuesta. Algunos bugs solo aparecen con usuarios reales. El session replay muestra lo que hace el código escrito por agentes para esos usuarios en producción, como un bucle de reintentos que satura un endpoint o un formulario que falla en silencio, y ahí es a menudo donde se hacen visibles las lagunas en estos fundamentos.

Git lo suficiente para revisar y deshacer los cambios de un agente

Git es la forma de aceptar parte del trabajo de un agente y deshacer el resto. Los agentes tienden a hacer ediciones de gran alcance, y aceptarlas de una sola vez oculta cambios no relacionados. Cuatro comandos cubren casi todo lo que necesitas:

git diff                 # read every change
git add -p               # stage hunk by hunk
git restore -p src/x.ts  # discard unwanted hunks
git revert <commit>      # undo a committed change

git add -p te pregunta por cada hunk antes de añadirlo al staging, y git restore -p descarta hunks del working tree uno a uno. git revert añade un nuevo commit que revierte uno anterior. Eso lo hace seguro en ramas compartidas, mientras que reset reescribe el historial.

Arquitectura suficiente para detectar una solución sobredimensionada

Una buena arquitectura resuelve el problema de hoy con lo que el proyecto ya tiene. Los agentes suelen recurrir a una nueva dependencia, o a una capa de factory o strategy, para resolver una tarea puntual. Instalar una librería de fechas para formatear una única fecha es un caso típico, ya que Intl.DateTimeFormat lo hace de forma nativa. Cuando un diff añade archivos, paquetes o abstracciones, pregúntate si el código existente podría hacer el trabajo con menos archivos. Si es así, rechaza el cambio o pide uno más pequeño.

Hábitos que te mantienen al mando

  • Pide al agente que explique sus decisiones. Si no puedes evaluar la explicación, ya sabes qué fundamento estudiar a continuación.
  • Lee el diff antes de aceptarlo. Revisa cada hunk, siempre.
  • Haz los cambios pequeños a mano. Escribir tú mismo un arreglo mantiene tu comprensión al día.

Los tests convierten tu criterio en comprobaciones que se ejecutan con cada cambio.

Conclusión

Los agentes pueden escribir el código, pero tú eres responsable de lo que hace. Aprende la semántica de HTTP, el modelo de renderizado del navegador, la propiedad del estado, los límites de seguridad, la depuración, Git y la contención necesaria para mantener las cosas pequeñas, y podrás rechazar un diff deficiente y explicar por qué. La próxima vez que un agente te entregue un cambio, empieza leyendo git diff y localiza una decisión que todavía no sepas explicar.

Preguntas frecuentes

¿Tiene axios el mismo problema con los errores HTTP que fetch?

No. Por defecto, axios rechaza la promesa ante cualquier código de estado de respuesta fuera del rango de 200 a 299, así que un 404 o un 500 acaba en el bloque catch. La opción validateStatus controla este comportamiento y se puede sobrescribir. Una API que devuelve 200 con un mensaje de error en el cuerpo sigue resolviéndose con éxito tanto en axios como en fetch, así que el servidor debe enviar los códigos de estado correctos.

¿Es seguro poner API keys en las variables de entorno de Vite?

Solo si la variable no lleva el prefijo VITE_. Vite expone las variables con prefijo VITE_ al código del cliente a través de import.meta.env y las reemplaza estáticamente en tiempo de compilación, por lo que sus valores acaban en el bundle del cliente. Las variables sin prefijo no se exponen al código del cliente. Al igual que NEXT_PUBLIC_ en Next.js, el prefijo indica que un valor debe publicarse y no lo protege, así que los secretos deben permanecer en un servidor.

¿Cómo renderizo de forma segura HTML proporcionado por el usuario cuando textContent no basta?

Sanea el HTML antes de insertarlo. OWASP recomienda DOMPurify: pasa la cadena por DOMPurify.sanitize y usa el resultado con innerHTML o dangerouslySetInnerHTML. No modifiques la salida saneada después, porque los cambios pueden reintroducir marcado peligroso. Mantén la librería de saneamiento actualizada, ya que se descubren nuevas formas de eludirla con regularidad. Los editores de texto enriquecido y el Markdown escrito por los usuarios son los casos típicos en los que se necesita.

¿Por qué mi efecto de React se ejecuta dos veces en desarrollo?

En desarrollo, el Strict Mode de React monta los componentes una vez más para poner al descubierto los efectos a los que les falta la función de limpieza. Esto no ocurre en producción. Si la segunda ejecución crea listeners, peticiones o suscripciones duplicados, el efecto necesita una función de limpieza que deshaga su configuración. Quitar el Strict Mode o añadir una guarda con un ref para saltarse la segunda ejecución solo oculta el bug.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.