From fbfa64509b1e72d017615c75c12cce70e3adac3d Mon Sep 17 00:00:00 2001 From: David Date: Thu, 8 Oct 2026 13:39:01 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20actualizar=20convenciones=20y=20a=C3=B1?= =?UTF-8?q?adir=20buenas=20pr=C3=A1cticas=20de=20implementaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 8247854..c08b0b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ No imprimir, copiar ni versionar .env locales o secretos. - Documentación en /docs, con nombres de ficheros en castellano. - Comentarios que expliquen decisiones, riesgos o contratos, nunca que repitan el código. - Mantener es y en para textos de interfaz; no insertar literales en áreas con i18n. -- En las sentencia condicionales (if) Utiliza siempre llaves cuando el bloque contiene una sola intrucción. +- Utilizar siempre llaves en if, else, for, while y estructuras equivalentes, incluso cuando el bloque contenga una sola instrucción. ## Disciplina @@ -97,3 +97,209 @@ Informar cambios, archivos principales, comandos ejecutados, resultados y pendie No afirmar validaciones no ejecutadas ni ocultar fallos conocidos. Precedencia: instrucciones actuales del usuario, AGENTS.md más cercano, este archivo y convenciones existentes. + + +## Buenas prácticas de implementación + +Estas reglas complementan las convenciones y documentos de arquitectura existentes. Antes de introducir un patrón nuevo, comprobar cómo resuelve el repositorio casos equivalentes y mantener coherencia con ellos. + +### Idioma + +- Responder, explicar decisiones, documentar cambios y redactar comentarios y TSDoc en castellano. +- Mantener código, identificadores, nombres de tipos, funciones, variables, componentes, hooks y ficheros en inglés. +- No traducir nombres de APIs, bibliotecas, protocolos ni conceptos técnicos cuando su denominación habitual esté en inglés. +- Los mensajes destinados al usuario final deben pasar por el sistema de internacionalización cuando corresponda. + +### TypeScript + +- Mantener TypeScript en modo estricto y resolver los errores de tipos en su origen; no debilitarlos para conseguir que compile. +- Preferir inferencia cuando el tipo sea evidente y anotaciones explícitas cuando formen parte de un contrato público o mejoren realmente la comprensión. +- Usar `unknown` para datos cuyo tipo todavía no haya sido validado. No sustituir `unknown` por `any` para evitar realizar narrowing. +- Validar todos los datos procedentes de fronteras externas: HTTP, base de datos cuando corresponda, variables de entorno, almacenamiento, mensajes, APIs externas y datos serializados. +- No considerar un cast `as Tipo` como validación. +- Preferir narrowing, type guards, schemas y discriminated unions frente a assertions. +- Preferir `satisfies` cuando se quiera comprobar que un valor cumple un contrato conservando su inferencia concreta. +- Evitar enums cuando una unión de literales o un objeto constante tipado represente mejor un conjunto cerrado. +- Hacer exhaustivos los `switch` sobre discriminated unions mediante `never` cuando sea útil. +- Evitar parámetros booleanos cuyo significado no resulte evidente en la llamada; utilizar opciones o tipos semánticos cuando mejoren la legibilidad. +- No crear tipos contenedores, aliases o genéricos que solamente oculten otro tipo sin aportar semántica. +- No duplicar tipos que puedan derivarse de una única fuente normativa. +- Mantener la opcionalidad exacta del dominio: distinguir cuando sea relevante entre propiedad ausente, `undefined` y `null`. +- Preferir estructuras inmutables cuando los datos no deban modificarse. +- No capturar excepciones únicamente para volver a lanzarlas sin aportar contexto, conversión o recuperación. +- No usar excepciones como flujo normal para resultados de negocio esperables. +- Las funciones deben tener contratos claros y evitar efectos laterales inesperados. + +### Node.js + +- No bloquear el event loop con operaciones síncronas de I/O dentro de rutas, servicios ejecutados por petición o procesos concurrentes. +- Preferir APIs asíncronas para filesystem, red, criptografía y procesos externos cuando formen parte del flujo de ejecución normal. +- Toda operación externa susceptible de quedar bloqueada debe disponer de timeout cuando la API utilizada lo permita. +- Utilizar `AbortSignal` o mecanismos equivalentes cuando sea necesario propagar cancelación. +- No dispersar accesos a `process.env` por la aplicación. Cargar, validar y normalizar la configuración en un punto definido durante el arranque. +- Fallar durante el arranque si falta configuración imprescindible, en lugar de descubrirla durante una petición. +- No mantener estado mutable global dependiente de una petición. +- No almacenar en memoria información cuya persistencia o coherencia dependa de varias instancias del servidor. +- Gestionar explícitamente el cierre ordenado de recursos persistentes cuando corresponda: servidor HTTP, conexiones de base de datos, colas y otros recursos externos. +- No ocultar `unhandledRejection`, `uncaughtException` ni errores de infraestructura para mantener artificialmente el proceso funcionando. + +### Express + +- Mantener las rutas y controladores pequeños. +- El controlador HTTP traduce entre HTTP y la aplicación; no contiene lógica de negocio. +- No acceder directamente al ORM desde routers o controladores salvo que la arquitectura documentada establezca expresamente otra cosa. +- Validar `params`, `query`, `headers` y `body` en la frontera HTTP antes de utilizarlos. +- No confiar en tipos TypeScript para considerar válidos datos recibidos por HTTP. +- Centralizar la transformación de errores a respuestas HTTP. +- No repetir bloques `try/catch` en cada handler si el mecanismo de errores de Express ya propaga correctamente los rechazos. +- Distinguir errores esperables de aplicación, errores de validación, autenticación/autorización, conflictos y errores inesperados de infraestructura. +- No enviar al cliente mensajes internos de excepciones, SQL, stack traces ni detalles de implementación. +- Una petición debe producir una única respuesta. Evitar ramas que puedan continuar después de enviar la respuesta. +- Los middleware deben tener una responsabilidad concreta y efectos explícitos. +- No utilizar middleware global para comportamiento que solo pertenece a una ruta o módulo. +- Mantener la composición de routers, middleware, controladores, casos de uso y repositorios según la arquitectura documentada del proyecto. + +### React 19 + +- Los componentes y hooks deben ser puros durante render. +- No producir efectos laterales durante render. +- No modificar props, estado, valores recibidos por hooks ni objetos pertenecientes a renders anteriores. +- No llamar componentes como funciones ordinarias; renderizarlos mediante JSX. +- Respetar estrictamente las Rules of Hooks. +- No utilizar hooks condicionalmente ni después de retornos que hagan variar su orden entre renders. + +#### Organización de componentes + +- Como norma general, cada fichero `.tsx` contiene un único componente React principal. +- No agrupar varios componentes funcionales relevantes en un mismo fichero por comodidad. +- Extraer un componente a su propio fichero cuando tenga props propias, estado, hooks, lógica de interacción, lógica de renderizado significativa o una responsabilidad identificable. +- Un subcomponente privado solo puede permanecer en el mismo fichero cuando sea pequeño, puramente presentacional, inseparable del componente principal y no dificulte la lectura. +- Si aparecen varios subcomponentes privados en el mismo fichero, reconsiderar la descomposición y extraerlos. +- No crear componentes únicamente para reducir el número de líneas de JSX. Extraer por responsabilidad, semántica o reutilización real. +- Evitar componentes monolíticos que mezclen consulta de datos, estado complejo, transformación de datos y grandes bloques de presentación. +- Mantener próximos entre sí el componente y los elementos que sean exclusivamente suyos, sin convertir los directorios en estructuras artificialmente profundas. + +#### Estado y efectos + +- Mantener el estado mínimo necesario. +- No guardar en estado valores que puedan calcularse correctamente a partir de props, estado existente o datos de consulta durante render. +- No utilizar `useEffect` para sincronizar un estado React con otro estado React. +- No utilizar `useEffect` para responder a una acción concreta del usuario; ejecutar esa lógica desde el event handler correspondiente. +- Utilizar efectos para sincronización real con sistemas externos, suscripciones, APIs imperativas del navegador u otros recursos externos. +- Todo efecto que cree una suscripción, listener, temporizador o recurso debe realizar su cleanup correspondiente. +- Diseñar efectos de forma que soporten correctamente montaje, desmontaje y nueva ejecución. +- No introducir `useMemo` o `useCallback` por defecto. Utilizarlos cuando exista una razón concreta de rendimiento, estabilidad referencial requerida por un contrato o evidencia obtenida mediante profiling. +- No duplicar en estado local datos gestionados por TanStack Query salvo que exista una necesidad explícita de edición local temporal. +- No copiar props a estado salvo que exista una semántica concreta que lo justifique. +- Preferir estado local al componente que lo consume y elevarlo únicamente cuando varios consumidores necesiten compartirlo. + +#### Hooks + +- Un custom hook debe encapsular comportamiento React reutilizable o una responsabilidad React claramente identificable. +- No crear hooks como simple sustitución de funciones normales. +- No crear hooks genéricos anticipando reutilización futura sin consumidores reales. +- Mantener los hooks independientes de detalles visuales cuando sea posible. +- No ocultar efectos importantes o llamadas remotas dentro de hooks con nombres ambiguos. + +#### Componentes visuales + +- Los componentes presentacionales no deben conocer endpoints, Express, Sequelize ni detalles de persistencia. +- Los componentes reutilizables no deben depender directamente de DTOs específicos de Metis Desk cuando puedan expresarse mediante props más pequeñas y semánticas. +- Preferir composición mediante `children` y props explícitas frente a componentes con multitud de flags booleanos. +- Evitar componentes con muchas variantes booleanas que produzcan combinaciones de estados difíciles de comprender. +- Mantener accesibilidad semántica: elemento HTML correcto, etiquetas, relaciones entre controles, navegación por teclado y foco. +- No implementar manualmente primitivas que shadcn/ui o Base UI ya proporcionen adecuadamente. + +### Formularios + +- Mantener una única fuente de verdad para cada campo. +- Utilizar React Hook Form cuando el formulario tenga complejidad suficiente y siguiendo los patrones existentes del repositorio. +- Utilizar schemas Zod como fuente normativa de validación cuando corresponda. +- No duplicar manualmente reglas de validación entre schema, formulario y llamada HTTP. +- Mostrar errores junto al contexto que los origina. +- Diferenciar errores de campo, errores globales del formulario y errores inesperados del servidor. +- Evitar envíos duplicados y reflejar visualmente estados pendientes. +- No limpiar o cerrar un formulario hasta confirmar que la operación que lo requiere se ha completado correctamente. + +### Acceso a datos en frontend + +- Centralizar query keys y mantenerlas estables. +- Las funciones de acceso a API no deben depender de componentes React. +- No realizar `fetch` directamente dentro de componentes si el módulo dispone de una capa de API o query establecida. +- No utilizar `useEffect` como sustituto de TanStack Query para cargar datos remotos. +- Después de una mutación, actualizar o invalidar únicamente las queries afectadas con una estrategia explícita. +- No invalidar indiscriminadamente toda la caché para evitar razonar sobre dependencias. +- Gestionar explícitamente estados de carga inicial, actualización, error, ausencia de datos y permisos insuficientes. + +### Estructura de ficheros + +- Cada fichero debe tener una responsabilidad reconocible. +- Evitar ficheros genéricos como `utils.ts`, `helpers.ts`, `common.ts`, `types.ts` o `constants.ts` convertidos en cajones de sastre. +- Ubicar utilidades, tipos y constantes junto al dominio o funcionalidad a la que pertenecen. +- Extraerlos a una ubicación compartida únicamente cuando existan consumidores reales en diferentes áreas. +- No crear ficheros `index.ts` o barrels indiscriminadamente. +- Evitar barrels que oculten dependencias, provoquen ciclos de importación o dificulten localizar el origen de un símbolo. +- No crear directorios con un único fichero sin que aporten una frontera conceptual útil. +- No reorganizar carpetas solamente por preferencia personal durante una tarea no relacionada. + +### Funciones y responsabilidades + +- Preferir funciones pequeñas con una responsabilidad definida, pero no fragmentar una operación sencilla en multitud de funciones triviales. +- Los nombres deben describir intención, no implementación accidental. +- Una función que realiza una consulta no debe modificar estado salvo que su contrato lo indique claramente. +- Separar cálculo puro de I/O cuando esa separación mejore pruebas y razonamiento. +- Reducir anidamiento mediante retornos tempranos cuando mejoren la legibilidad. +- No utilizar retornos tempranos que oculten invariantes o hagan difícil seguir el flujo. +- No crear parámetros, opciones o extensibilidad que ningún consumidor necesite actualmente. + +### Dependencias + +- Antes de añadir una dependencia, comprobar si el proyecto o la plataforma ya resuelven el problema. +- No instalar una biblioteca por una única función trivial que pueda implementarse claramente con APIs estándar. +- No sustituir una dependencia ya establecida por otra equivalente sin una razón explícita. +- Comprobar compatibilidad con las versiones reales de Node.js, React, TypeScript y demás dependencias del repositorio. +- No asumir APIs de una versión distinta a la instalada. + +### Errores y observabilidad + +- Los errores deben conservar causa y contexto suficiente para poder diagnosticarlos. +- No registrar el mismo error repetidamente en todas las capas. +- Registrar el error en la capa responsable de observarlo o resolverlo. +- No registrar tokens, contraseñas, cookies, cuerpos sensibles ni credenciales. +- No utilizar `console.log` como mecanismo permanente de observabilidad cuando exista infraestructura de logging. +- Los mensajes de error internos deben ser útiles para diagnóstico; los destinados al usuario no deben revelar detalles internos. + +### Pruebas + +- Probar comportamiento observable y contratos, no detalles internos de implementación. +- No escribir tests cuyo único objetivo sea aumentar cobertura. +- Evitar mocks cuando pueda utilizarse una dependencia real rápida y determinista o un fake sencillo. +- Mockear fronteras externas, no cada función interna. +- Mantener los tests legibles y con preparación mínima. +- Cada corrección de un bug reproducible debe considerar una prueba que falle antes de la corrección y pase después. +- Para lógica con estados cerrados, cubrir los casos relevantes y los límites, no solamente el camino feliz. + +### Criterio antes de escribir código + +Antes de implementar: + +1. Buscar implementaciones equivalentes dentro del repositorio. +2. Consultar la documentación normativa del proyecto aplicable. +3. Identificar qué capa es responsable del comportamiento. +4. Comprobar si ya existe un componente, hook, schema, utilidad, Value Object o servicio reutilizable. +5. Elegir la solución más pequeña que respete los contratos existentes. +6. Evitar introducir una abstracción nueva si un patrón existente resuelve adecuadamente el problema. + +Durante la implementación: + +- No ampliar el alcance para corregir cuestiones no relacionadas. +- Si se detecta una mejora ajena a la tarea, mencionarla al finalizar en lugar de modificarla automáticamente. +- No alterar APIs públicas, contratos HTTP, schemas, migraciones o comportamiento compartido como efecto secundario accidental. +- No crear compatibilidad hacia atrás que nadie haya solicitado cuando se pueda actualizar de forma controlada a todos los consumidores. +- No dejar código muerto, implementaciones alternativas comentadas ni TODO vagos. + +Al finalizar: + +- Revisar el diff completo, no únicamente los ficheros editados individualmente. +- Comprobar que no se hayan introducido duplicaciones, componentes excesivamente grandes, varios componentes relevantes por fichero, imports no usados, casts innecesarios ni efectos React evitables. +- Ejecutar las validaciones apropiadas para el alcance real del cambio. \ No newline at end of file