Buscar/Mantener una base de código grande y modular fácil de leer y de cambiar sin romperla

Mantener una base de código grande y modular fácil de leer y de cambiar sin romperla

Úsalo cuando una IA trabaja en una base de código grande dividida en muchos componentes. Da reglas sobre estilo de código, tamaño de archivos y cambios, revisión de código, pruebas, nombres de la API y builds, para que los cambios sigan pequeños, legibles y no rompan otras partes.

Para usar estas reglas de IA
## Evitar argumentos sueltos como true/false para que cada llamada se entienda sola Prioriza la claridad en el punto de llamada por encima del ingenio. - Inserta las variables directamente en los marcadores de posición del formateo de cadenas en lugar de pasarlas de forma posicional. - Prefiere las referencias a métodos frente a los cierres (closures) triviales cuando el lenguaje lo permita. - Evita los argumentos posicionales opacos —booleanos sueltos, null/none, o literales numéricos— que obligan al lector a adivinar su significado (por ejemplo, `foo(false)`). Prefiere enums, métodos con nombre, newtypes u otras formas de API autoexplicativas. - Cuando una forma autoexplicativa sea imposible y deba permanecer un literal posicional, anótalo con un comentario en línea `/*param_name*/` que coincida exactamente con el nombre del parámetro de la función llamada. Deja sin anotar los literales de cadena/carácter a menos que el comentario realmente aporte claridad. - Haz que las ramificaciones sean exhaustivas (sin brazos comodín que capturen todo) para que los casos añadidos posteriormente se detecten en tiempo de compilación. - Dota a las abstracciones recién añadidas (traits/interfaces) de comentarios de documentación que expliquen su función y cómo deben usarlas quienes las implementen. - Sigue las convenciones locales del archivo. No alternes el código entre formas equivalentes sin una ganancia clara de legibilidad o funcionalidad, y no añadas anotaciones de tipo únicamente para satisfacer una forma abreviada. - No crees funciones auxiliares pequeñas que se referencien una sola vez. ## Mantener archivos y cambios pequeños sin inflar el componente central Mantén las unidades pequeñas para que los cambios no relacionados dejen de chocar entre sí. ### Tamaño de los módulos - Prefiere añadir un módulo nuevo antes que hacer crecer uno existente. Procura que los módulos tengan menos de unas 500 líneas de código que no sea de pruebas; en cuanto un archivo se acerque a las ~800 líneas, coloca la nueva funcionalidad en un módulo nuevo, salvo que exista una razón sólida y documentada para no hacerlo. - Aplica esto de forma más estricta a los archivos de orquestación central y de alto tráfico que ya atraen ediciones no relacionadas; mantén esos archivos centrados en la orquestación en lugar de dejar que acumulen funciones auxiliares independientes. - Al extraer código de un módulo grande, traslada sus pruebas y la documentación de tipos/módulo junto con la nueva implementación, de modo que los invariantes permanezcan cerca del código que los posee. ### Resiste la tentación de inflar el componente más grande - El componente más grande o más central es el camino de menor resistencia para el código nuevo, y precisamente por eso sigue creciendo. Resiste la tentación de añadirle más código. Pregúntate primero si un componente existente es un mejor lugar, o si ha llegado el momento de introducir uno nuevo, refactorizando según sea necesario. En la revisión, cuestiona los cambios que agrandarían el componente central de forma innecesaria. ### Tamaño de los cambios - A menos que un cambio sea mecánico, mantén el total de líneas modificadas por debajo de ~800; para lógica compleja, por debajo de ~500. - Si un cambio es más grande, divídelo en etapas revisables y entrega primero la etapa coherente más pequeña, basando la división en el diff real, las dependencias y los puntos de llamada afectados. ## En la revisión de código, detectar cambios incompatibles y un contexto que crece sin límite Revisa buscando los riesgos que resultan costosos de detectar más adelante. ### Mantén pequeña la superficie pública Mantén la API pública/exportada de cada componente lo más pequeña posible. Prefiere los internos privados con una superficie pública explícitamente exportada, y evita la proliferación de funciones auxiliares solo para pruebas dentro del código de producción. ### Protege el contexto del modelo/inferencia Cuando un sistema mantiene un contexto que se envía a un modelo en cada solicitud de inferencia, protégelo: - Construye el contexto de forma incremental; nunca reescribas el historial. - Evita los cambios frecuentes de contexto que invalidan la caché. - Todo lo que se inyecte debe tener un tamaño acotado y un límite estricto: nada de elementos sin límite, y ningún elemento individual debe superar un tope fijo de tokens. - Marca para revisión manual adicional cualquier elemento nuevo que pueda superar por sí solo un umbral de tamaño considerable. - Enruta todos los fragmentos inyectados a través de una única abstracción tipada en lugar de insertarlos de forma improvisada. ### Detecta cambios incompatibles Busca de forma activa cambios incompatibles (breaking changes) en las superficies de integración externas: APIs públicas, payloads de eventos (incluso los experimentales), parámetros de línea de comandos, carga de configuración y la capacidad de reanudar sesiones existentes o estado persistido. ## Comprobar el comportamiento real con pruebas de integración y de snapshot Prueba el comportamiento, mantén las pruebas honestas y manténlas fuera del código de producción. - Para los cambios en el comportamiento central, prefiere las pruebas de integración que ejerciten una instancia real frente a las pruebas unitarias limitadas. Cualquier cambio en ese comportamiento DEBE añadir una prueba de integración; enumera los comportamientos que necesitan cobertura. - Coloca las pruebas unitarias en un archivo de pruebas hermano dedicado en lugar de en línea dentro del archivo de implementación, y dale al archivo un nombre descriptivo. No añadas funciones exclusivas para pruebas a los módulos de producción, y no reescribas los módulos de prueba en línea existentes solo para seguir esta convención. - Cualquier cambio en la salida renderizada visible para el usuario debe incluir cobertura de snapshots: añade o actualiza el snapshot y revisa el diff como parte del cambio, para que el impacto en la interfaz siga siendo visible. - Verifica objetos completos con igualdad profunda en lugar de comparar campos de uno en uno, usando una librería de aserciones que produzca diffs claros. - No mutes el estado global del proceso (por ejemplo, variables de entorno) dentro de las pruebas; pasa las dependencias y los flags desde fuera. - No pruebes constantes definidas de forma estática, y no añadas pruebas negativas para lógica que ha sido eliminada. - Comprueba si ya existen ayudantes de prueba antes de escribir una configuración nueva, para que las pruebas se mantengan simplificadas y legibles. ## Usar los mismos nombres y formatos de campos en toda la API Al evolucionar una API en red o entre lenguajes, la consistencia en el límite de comunicación (wire boundary) evita roturas silenciosas. - Concentra el desarrollo activo en la versión actual de la API; no añadas nueva superficie a las versiones obsoletas. - Nombra los payloads de forma consistente según su rol (un sufijo para los parámetros de solicitud, otro para las respuestas, otro para las notificaciones). Expón los métodos como un nombre de recurso seguido de un nombre de acción, con una convención singular/plural consistente para el recurso. - Fija una única convención de mayúsculas/minúsculas (casing) para los campos y los valores de enum en el límite de comunicación, y aplícala en todas partes, sobrescribiéndola solo por necesidades explícitas de compatibilidad. Mantén alineadas las anotaciones del serializador y las de generación de tipos para que ambos lados coincidan; cuando uno use un renombrado explícito, el otro debe coincidir. - Para las uniones discriminadas, usa etiquetado explícito en todos los serializadores. - Prefiere IDs de tipo cadena simple en el límite de comunicación y conviértelos internamente. Usa marcas de tiempo enteras con una nomenclatura `*_at` consistente. - Para los endpoints de listado, implementa paginación por cursor de forma predeterminada (un cursor y un límite como entrada; un array de datos y un next-cursor como salida). - Restringe la superficie experimental detrás de un marcador explícito, y regenera la documentación, los ejemplos y los fixtures de esquema cada vez que cambien las formas de la API. ## Ejecutar primero el formateador y pruebas acotadas, sin tocar las protecciones del sandbox Respeta la cadena de herramientas y las restricciones que ya existen. ### Protecciones de entorno y sandbox - Nunca añadas ni modifiques código que gestione los flags de capacidad de sandbox o de entorno sin una intención deliberada. Estas protecciones existen porque el código y las pruebas se diseñaron en torno a límites de tiempo de ejecución específicos; por ejemplo, pruebas que finalizan anticipadamente cuando una capacidad como el acceso a la red no está disponible. Eliminar o alterar una protección de forma silenciosa rompe esos supuestos. ### Higiene de compilación y de la cadena de herramientas - Ejecuta el formateador automáticamente al terminar los cambios de código; no pidas aprobación para formatear. - Ejecuta primero las pruebas más específicas y relevantes, limitadas al componente modificado. Amplía a la suite completa solo cuando haya cambiado código compartido o central, y confirma antes de iniciar ejecuciones largas de la suite completa. - Cuando cambies entradas que alimentan artefactos generados —esquemas, lockfiles, tipos generados—, regenéralos en el mismo cambio; la desviación (drift) se verifica en CI. - Si la herramienta de compilación necesita que se declaren archivos fuente para el acceso en tiempo de compilación, actualiza esa declaración cada vez que añadas ese tipo de lecturas. - Ten paciencia con los comandos de compilación y de pruebas lentos; la contención de bloqueos (lock contention) esperada puede ralentizarlos. No los mates por ID de proceso. ### Soporte de plataformas - Las pruebas y las funcionalidades deben ser compatibles con todos los sistemas operativos oficialmente admitidos, a menos que una funcionalidad sea explícitamente específica de un sistema operativo. Cuando el sistema permita que los componentes se ejecuten en distintos sistemas operativos, asegúrate de que las pruebas nuevas ejerciten esas configuraciones multiplataforma.