alii13/overfetch

Audita cómo una aplicación frontend se comunica con su backend y genera un informe clasificado y basado en evidencia sobre la amplificación de solicitudes con correcciones concretas: lecturas N+1 por fila, búsquedas duplicadas, temporizadores de polling apilados, frescura cero, invalidación generalizada, cascadas en serie, paginación de

¿Qué es overfetch?

Esta habilidad para agentes de IA, llamada 'overfetch', permite realizar una auditoría exhaustiva de las solicitudes de red que realiza una aplicación frontend hacia su backend. El objetivo es identificar y clasificar patrones de amplificación de solicitudes, como lecturas N+1, duplicados, temporizadores de polling en cascada, falta de caché adecuada, invalidaciones globales, y sobrecarga de datos. El agente produce un informe detallado en Markdown que incluye evidencia de cada hallazgo (con líneas de código o capturas de red), la causa raíz, el patrón de corrección y el impacto esperado en el número de solicitudes. La herramienta se usa en conjunto con scripts de Python (static_scan.py, coverage_map.py, har_analyze.py) que analizan el código fuente y las capturas de red (HAR) para encontrar candidatos, verificar su validez y medir el impacto real. Es compatible con proyectos que usan bibliotecas como TanStack Query, SWR, Apollo y RTK Query, y se puede integrar en flujos de trabajo de desarrollo para mejorar el rendimiento y la eficiencia de las aplicaciones web.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/alii13/overfetch/tree/HEAD

Installed? Explore more Investigación y análisis de datos skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

overfetch: auditoría de solicitudes

Estás produciendo un informe en el que alguien actuará: una lista clasificada de las formas en que esta aplicación realiza más solicitudes de las que sus pantallas necesitan, cada una con evidencia, una corrección y el número que debería tener. El informe solo es útil si cada hallazgo sobrevive a un lector escéptico, por lo que el método a continuación dedica la mayor parte de su esfuerzo a la verificación y a decir claramente qué se verificó y qué no.

Lo que entregas

Un informe en Markdown (plantilla en references/report-template.md) con:

  1. Alcance y evidencia: qué árbol, qué commit, qué capturas.
  2. Cobertura: cada ruta, fragmento diferido y sitio de datos protegido, y cuáles de ellos se leyeron solo en código versus cuáles se ejercitaron en una captura.
  3. Hallazgos clasificados por cómo crece el recuento de solicitudes, cada uno con archivo:línea o evidencia de captura, causa raíz, patrón de corrección, delta esperado, esfuerzo.
  4. No-hallazgos: patrones que verificaste y descartaste, con el motivo. Estos evitan que la siguiente persona vuelva a investigar.
  5. Decisiones de producto abiertas y peticiones al backend: cosas que necesitan un humano o un cambio de API, no un parche del cliente.
  6. Límites: lo que la auditoría no pudo medir.

Ofrece implementar correcciones después, una PR por hallazgo, comenzando por la de mayor severidad. No comiences a corregir durante la auditoría.

Principios

Un acierto de expresión regular es un candidato, no un hallazgo. scripts/static_scan.py lee texto. No puede distinguir una barra de pestañas de 3 filas de una tabla de 3000 filas, o una invalidación raíz deliberada después de una importación masiva de una perezosa. Lee el código detrás de cada candidato que pretendes informar. Di lo que leíste.

Lo medido gana a lo afirmado. Una captura de la aplicación en ejecución es la verdad sobre los recuentos de solicitudes; el código es la verdad sobre el por qué. Los comentarios, documentos y registros de auditoría antiguos son afirmaciones. Cuando un documento dice "fresco durante 60 segundos" y el código dice staleTime: Infinity, informa el código, anota la contradicción y registra el hash del commit que leíste.

Conoce la semántica real de la biblioteca antes de culparla. Los errores que más cuestan son afirmaciones seguras que son incorrectas: "el caché lo des-duplica" cuando las claves difieren, "el polling se pausa cuando está inactivo" cuando solo se pausa cuando está oculto, "tres llamantes, tres búsquedas por minuto" cuando la biblioteca vuelve a sincronizar temporizadores de la misma clave y comparte la búsqueda. references/library-facts.md contiene los datos para TanStack Query, SWR, Apollo y RTK Query, con la ruta de origen con la que se verificó cada uno. Lee la sección de la biblioteca en uso antes de escribir cualquier frase sobre almacenamiento en caché o polling, y cuando una afirmación decida un hallazgo, abre el código fuente de la biblioteca instalada y confírmalo.

Clasifica por crecimiento, no por recuento. Una lectura que escala con las filas, con los componentes montados o con el tiempo es peor que un duplicado fijo. La rúbrica de severidad a continuación trata sobre la clase de crecimiento.

La cobertura tiene un denominador. Una captura solo ve el código que se cargó mientras se ejecutaba: las rutas que alguien visitó, los diálogos y pestañas que alguien abrió, las banderas que estaban activadas, los estados que el backend devolvió. Los fragmentos diferidos, las pantallas protegidas y las rutas de error son exactamente donde se esconden los errores de solicitud, porque nadie los mira durante el desarrollo. Por lo tanto, cada auditoría comienza desde la superficie completa (coverage_map.py), cada "sin problema" se limita a lo que se cubrió, y el informe dice qué partes se ejercitaron en vivo y cuáles solo se leyeron.

Di lo que está bien. Una precarga al pasar el ratón que es una lectura desduplicada por fila es un no-hallazgo. Escríbelo como tal. Un informe que solo enumera problemas hace que el lector se pregunte qué más se pasó por alto.

Los números provienen de algún lugar. Cada recuento en el informe se remonta a un archivo de captura o una ruta de código. No inventes "6+" o "aproximadamente 100" por intuición; si no puedes medirlo, di "no medido" y proporciona la fórmula (filas × lecturas por fila).

Flujo de trabajo

Informa al usuario de antemano cuánto tiempo lleva. Una auditoría solo estática de una aplicación de tamaño mediano requiere de 15 a 30 minutos de lectura. Agregar capturas en vivo añade de 15 a 45 minutos dependiendo de cuántos recorridos sean importantes.

1. Alcance

  • Encuentra la(s) raíz(ces) de la aplicación y la biblioteca de datos desde package.json (el escaneo imprime ambas). Monorepos: escanea cada aplicación por separado.
  • Encuentra la capa de datos: fábricas de claves de consulta, módulos de transporte/servicio, la construcción de QueryClient (o configuración SWR, cliente Apollo, createApi de RTK). Lee primero los valores predeterminados; establecen la línea de base con la que se mide cada hallazgo.
  • Enumera las pantallas que importan: generalmente la ruta de inicio después de iniciar sesión, la lista más concurrida y cualquier pantalla que permanezca abierta durante horas (un shell, un monitor). Pregunta al usuario si no es obvio; de lo contrario, elige esas tres y dilo.
  • Registra el commit: git rev-parse HEAD. Cada línea del informe se refiere a esta revisión.
  • Decide si una captura en vivo es posible: un servidor de desarrollo en ejecución más un navegador que puedas manejar, o un HAR que el usuario ya tenga. Si no es así, la auditoría es solo estática y el informe debe decirlo en su primer párrafo.

2. Mapa de cobertura

python3 <skill-dir>/scripts/coverage_map.py <app-root> --format md --out coverage.md

Este es el denominador. Enumera cada ruta (enrutadores basados en archivos para TanStack Router, Next.js, Remix, SvelteKit y SolidStart; enrutadores definidos en código por sus literales de ruta; archivos de entrada en caso contrario), cada límite dinámico import() con las lecturas detrás de él, cada módulo de datos protegido por una bandera, plataforma, permiso o verificación de entorno, cada literal de ruta de endpoint en el código fuente, y los módulos de datos a los que ninguna ruta llega. Se ejecuta en unos segundos en una aplicación de 2000 archivos.

Úsalo de tres maneras:

  • Para planificar. El plan de captura en la parte inferior nombra las rutas con más sitios de lectura, los fragmentos diferidos a activar y las puertas a alternar. Úsalo para elegir los recorridos en el paso 6 en lugar de adivinar.
  • Para delimitar el escaneo. Un hallazgo en un módulo al que ninguna ruta llega es código muerto o al que se llega por algo que el mapa no puede resolver. Verifica cuál antes de llamarlo en vivo.
  • Para cerrar el círculo. Después de capturar, vuelve a ejecutarlo con las capturas; marca qué rutas se visitaron, qué módulos y fragmentos diferidos se cargaron (exactamente, para una captura de servidor de desarrollo), qué módulos protegidos se ejecutaron y qué rutas de endpoint aparecieron:
python3 <skill-dir>/scripts/coverage_map.py <app-root> --har home.har --har library.har --visited home.har.visited.txt --format md

Repite las capturas hasta que las rutas y los fragmentos diferidos con la mayoría de los sitios de lectura estén cubiertos, o escribe por qué no lo están (sin datos de prueba, necesita un rol que no tienes, plataforma que no puedes ejecutar).

3. Escaneo estático

python3 <skill-dir>/scripts/static_scan.py <app-root> --format md --out scan.md
python3 <skill-dir>/scripts/static_scan.py <app-root> --format json --out scan.json

El escaneo es rápido (unos diez segundos para 2000 archivos) y solo necesita Python 3.9+. Emite candidatos con una severidad, una confianza, el detector que se activó, archivo:línea, por qué es importante, el patrón de corrección y una instrucción "verificar primero". Lee scan.md completo; el JSON es para scripts y para el arnés de evaluación.

Agrega --exclude para árboles generados que los valores predeterminados pasan por alto, --min-confidence medium cuando la lista es larga y deseas que la primera pasada sea ajustada.

4. Verificar candidatos

Trabaja a través de los candidatos en este orden: P0 por confianza, luego P1, luego el resto. Para cada uno que conserves, debes haber abierto el archivo y poder responder:

  • ¿Cuál es el multiplicador por fila o por observador, y qué lo limita? (Un .map sobre una enumeración fija no es N+1. Un recuento de filas de una lista paginada lo es.)
  • ¿El caché ya lo absorbe? (Misma clave, entrada cálida, lecturas de enabled: false, un padre que ya buscó el objeto.)
  • ¿Hay un endpoint por lotes o de proyección que el cliente pueda usar en su lugar? Lee el cliente API o la especificación OpenAPI. Si no existe ninguno, la corrección es una solicitud al backend y el hallazgo permanece con "bloqueado por backend".
  • ¿Cuál es el patrón de corrección, por nombre, de references/catalog.md?

Descarta los candidatos que no pasen estas comprobaciones y conserva aquellos que descartes y que un lector podría plantear de manera plausible: van a la tabla de no-hallazgos con una razón de una línea.

La entrada del catálogo para cada detector enumera las condiciones específicas de no hallazgo. Léelo para cada ID de detector que aparezca en el escaneo; está organizado por los mismos ID.

5. Comprobaciones transversales que el escáner no puede realizar

Estas necesitan lectura, no búsqueda con grep. Hazlas todas; la mayoría de las auditorías encuentran al menos una.

  • Canonicidad de claves. Elige los cinco endpoints más leídos y encuentra cada clave de consulta que lea cada uno. Dos claves para una lectura son dos entradas de caché y dos búsquedas. Busca especialmente lecturas de listas bajo diferentes proyecciones (completa vs solo nombres) y diferencias de alcance (org vs espacio de trabajo) que no son realmente datos diferentes.
  • Lecturas en vuelo compartidas. Dos componentes que se montan en el mismo marco con la misma clave comparten una sola solicitud solo si la clave es idéntica en ese momento. Las claves creadas a partir de un estado que se estabiliza un tick después (una identificación que llega de otra consulta) producen dos búsquedas: una con undefined, una real. Verifica las puertas enabled en las claves dependientes.
  • Frescura por superficie. Haz una tabla de staleTime por familia de datos (identidad, listas, detalles, estado en vivo) y compara con la rapidez con la que cada una cambia realmente. El error habitual es un valor global que no es correcto para nada.
  • Propiedad del polling. Para cada opción de polling en un hook compartido o fábrica de opciones, enumera los componentes que se pueden montar a la vez y la clave que usa cada uno. En SWR y Apollo, cada instancia ejecuta su propio temporizador y las búsquedas se multiplican a menos que caigan en una ventana de desduplicación. En TanStack Query, los temporizadores se reinician en cada actualización de consulta y comparten una búsqueda, por lo que los observadores de la misma clave cuestan una búsqueda por intervalo más corto; la acumulación se muestra entre claves o clientes, y el hallazgo es la propiedad de la cadencia. En RTK Query, un temporizador por entrada de caché se ejecuta en el intervalo más corto.
  • Enfoque y visibilidad. ¿Qué activa las recargas por enfoque en esta plataforma? Un shell de escritorio (Tauri, Electron) a menudo conecta el enfoque de la ventana del SO, que se activa con mucha más frecuencia que la visibilidad de la pestaña. ¿Qué polling se pausa cuando está oculto y cuál sigue ejecutándose mientras el usuario está inactivo en una pantalla visible?
  • Techo de concurrencia. ¿Hay un limitador para lecturas de abanico y cuál es? Ten en cuenta que un limitador de 6 en vuelo no es un límite de 6 solicitudes por segundo; no escribas uno como el otro.
  • Contrato de paginación. Para colecciones con total desconocido, ¿el cliente se detiene cuando una página vuelve corta o sigue preguntando hasta que llega una página vacía? Verifica los tamaños de página con lo que la pantalla puede mostrar.
  • Reintentos. ¿Qué hace el cliente en 403/404/422? El valor predeterminado de la biblioteca reintenta tres veces; un N+1 de 100 filas con un fragmento prohibido se convierte en 400 solicitudes.
  • Persistencia. Si el caché persiste (IndexedDB, localStorage, un almacenamiento nativo), ¿cuál es maxAge, qué lo invalida al implementar y el almacenamiento es por plataforma? Un caché persistente cambia lo que significa "carga en frío" en una captura; di cuál mediste.
  • Mutaciones. Para las cinco escrituras más comunes, ¿qué se invalida? Las invalidaciones raíz después de una escritura pequeña son el hallazgo habitual.

6. Captura en vivo

Si puedes ejecutar la aplicación o el usuario te proporcionó una captura, obtén números. references/capture.md tiene los recorridos a ejecutar, la matriz de estados (vacío, poblado, error, lento, sin conexión, inactivo, oculto, protegido, caché fría y cálida), cómo capturar (HAR de DevTools, Playwright, scripts/capture.mjs incluido, o el registro de solicitudes de la propia aplicación) y cómo mantener las capturas comparables.

Elige los recorridos del plan del mapa de cobertura, no por costumbre. Prefiere el servidor de desarrollo cuando puedas ejecutarlo: sus capturas nombran cada módulo que se cargó, por lo que el mapa puede decir exactamente qué fragmentos diferidos y módulos protegidos se ejecutaron. Luego vuelve a ejecutar el mapa con las capturas (paso 2) y captura las brechas o enuméralas en Límites.

Una captura muestra lo que sucedió, nunca por qué. En una auditoría solo de captura, matiza cada cláusula causal donde se encuentre ("consistente con dos claves de consulta", "probablemente un remontaje"), no una vez en un descargo de responsabilidad al principio: los lectores citan oraciones individuales, y un "porque" sin matices viaja sin su descargo.

Cuando todo lo que tienes es una captura que otra persona tomó, no puedes ver lo que se perdió solo con la captura. Dilo, enumera lo que una sola sesión generalmente pierde (rutas no visitadas, diálogos y pestañas no abiertos, variantes de banderas y roles, estados de error y vacío, comportamiento de inactividad y pestaña oculta) y recomienda las capturas que lo cerrarían.

python3 <skill-dir>/scripts/har_analyze.py capture.har --format md --out capture.md

Informa solicitudes de API por página, ráfagas por fila en una plantilla de ruta, polling con el número de temporizadores apilados por clave, duplicados dentro de una ventana (con hash de cuerpo para POST), cadenas en serie, errores repetidos y las respuestas más grandes. Los activos estáticos están excluidos; usa --ignore para hosts de telemetría.

Dos cosas a tener en cuenta al leerlo:

  • Una POST a la misma URL dos veces es un duplicado solo cuando el hash del cuerpo coincide. Si los cuerpos no fueron capturados, la herramienta dice "no verificado"; repórtalo de esa manera.
  • Una cadena en serie es una cascada solo si la solicitud posterior no necesitaba nada de la anterior. La herramienta encuentra la forma; tú confirmas la dependencia en el código.

7. Conciliar

Empareja cada hallazgo estático con evidencia de captura donde una captura cubra esa pantalla. Cuando estén en desacuerdo, la captura gana para los recuentos y el código gana para la causa. Cuando una captura muestra una amplificación que ningún candidato explica, lee el código hasta que lo haga; suele ser un problema de canonicidad de claves o de claves dependientes del paso 4.

Si el repositorio tiene una auditoría anterior o un documento de rendimiento, verifica cada una de sus afirmaciones que toquen tus hallazgos en el código en este commit y enumera las contradicciones en el informe. Las afirmaciones obsoletas son cómo el mismo error se "corrige" dos veces.

8. Clasificar y escribir

Usa la rúbrica:

GravedadClase de crecimientoFormas típicas
P0Crece sin techo: con filas, con observadores montados o con el tiempoconsulta por fila en una lista paginada, temporizadores de polling por instancia en un hook compartido (SWR, Apollo, hecho a mano), invalidación en cada pulsación de tecla, intervalo no limpiado
P1Multiplicador fijo en una ruta activastaleTime 0 en una lista que cada pantalla lee, recarga en cada enfoque, búsqueda impulsada por efecto sin caché

...

Hallazgos P2 y P3 están documentados en SEVERITY_RUBRIC.md. La idea clave es clasificar por cómo el problema escala, no solo por el número actual de solicitudes. Las correcciones deben implementarse en orden de gravedad, una por Pull Request.

Skills relacionados