13 readiness dimensionsweights change by archetype type_safety modularity maintainability accessibility performance observability testability debuggability feature_ext cloud_scale api_migration test_surface onboarding api_backend archetype positioning_doc archetype weights change by archetype — and that's the contract

The 13 dimensions I use to score LLM-generated code

1 de junio de 2026·Implementations

"Production-ready" es una palabra que dejó de significar algo. La uso un equipo y quiere decir "compila, tiene tests, no se cae en staging". La usa otro y quiere decir "pasa SOC2, tiene observabilidad estructurada, runbook documentado, on-call rotation". La usa un tercero y quiere decir "el cliente la firmó". Cuando se la pasamos a un LLM que está generando código, el modelo no tiene anclaje — optimiza lo que cree que queremos, que casi nunca es lo que necesitamos.

Llevo un tiempo dándole vueltas a esto y la salida que encontré no es una definición mejor. Es descomponer la palabra en dimensiones medibles. Trece, en mi caso. No porque sean las únicas correctas — son las que aterrizaron para los arquetipos que tengo en agentguard-lib. La idea de fondo es compartir un vocabulario común con el LLM que está generando, y de paso con el equipo que va a revisar.

Lo separo en cinco pedazos: (a) por qué la palabra falla como métrica, (b) cuáles son las 13, (c) por qué los pesos cambian según arquetipo, (d) cómo se evalúa, (e) qué no resuelve.

Por qué "production-ready" falla como métrica

Si le pido a tres senior engineers que evalúen el mismo PR contra "production-ready", obtengo tres veredictos distintos. Cada uno tiene un modelo mental implícito de qué pesa más: uno mira type safety, otro mira observabilidad, otro mira si el código "se entiende". Ninguno está equivocado. El problema es que el modelo mental no está escrito en ningún lado, y cuando dos personas discrepan, la discusión es sobre prioridades implícitas, no sobre el código.

Un LLM tiene el mismo problema, amplificado. Cuando le decimos "hazlo production-ready", el modelo aplica un promedio de todo lo que vio en su entrenamiento. Promedio de buenas y malas prácticas, promedio de stacks de distintas eras, promedio de niveles de criticidad. El resultado es código que parece serio pero no está optimizando hacia ninguna restricción explícita. Es "production-ready" para un proyecto promedio en GitHub — que no es el tuyo.

En code review humano pasa lo mismo. "Esto no está listo para producción" es un comentario que no es accionable. Quien lo recibe no sabe si el problema es performance, si es testability, si es onboarding de quien venga después. Necesita una segunda iteración solo para entender qué hay que arreglar. Y muchas veces el revisor mismo no tiene claro qué dimensión lo está incomodando — siente que algo no cuadra, pero no logra nombrarlo.

La parte que más me sorprendió cuando empecé a observar este patrón es que el costo no es sólo de tiempo. Es de confianza. Cuando alguien recibe "no está listo" sin un eje concreto, lo que aprende es que la barra es subjetiva. La próxima vez va a tratar de adivinarla en vez de medirla. Y eso es exactamente el modo en que se construye deuda silenciosa en un equipo.

La amplificación que trae la IA es que ahora no es una persona generando código a velocidad humana — son agentes generando código a velocidad de máquina. La barra subjetiva, que en ese contexto era ineficiente pero manejable, se vuelve directamente insostenible. Cuando el volumen sube dos órdenes de magnitud, cualquier criterio implícito colapsa por peso propio.

Las 13 dimensiones

Las que terminé usando son: type_safety, modularity, maintainability, accessibility, performance, observability, testability, debuggability, feature_extensibility, cloud_scalability, api_migration_cost, test_surface, team_onboarding. Cada una se puntúa entre 0.0 y 1.0 dentro del arquetipo, y el peso de cada dimensión depende del arquetipo — no del proyecto, sino del tipo de proyecto.

La primera observación honesta: no son ortogonales. Hay overlap entre testability y test_surface, hay overlap entre maintainability y team_onboarding, hay overlap entre debuggability y observability. Y eso está bien. Lo que importa no es que sean disjuntas — es que sean explícitas. Cuando el LLM ve "type_safety: 0.9, observability: 0.9" en el contrato, no tiene que adivinar qué priorizar; tiene un norte numérico.

Cada dimensión tiene una definición operacional asociada en la lib. type_safety no es "usa tipos" — es una rúbrica con criterios verificables: hay anotaciones en signatures, hay validación en boundaries, hay handling de Union types donde corresponde. observability no es "tiene logs" — es una rúbrica que pregunta por logs estructurados, por correlation IDs, por métricas exportables. Esa precisión es la que vuelve la nota más que una opinión.

Las 13 tampoco son universales. Son las que cubren los arquetipos que ya tengo construidos en agentguard-lib: api_backend, library, cli_tool, react_spa, web_app, script, entre otros. Si alguien construye un arquetipo para un sistema embebido, probablemente necesita dimensiones que yo no tengo — power_budget, real_time_guarantees, memory_footprint_static. Si alguien construye un arquetipo para data engineering, va a necesitar schema_evolution_cost o replay_safety. La lista no es el aporte. El aporte es la idea de que la lista tiene que existir en algún lado, escrita, versionada, discutible.

Por qué los pesos cambian según arquetipo

Aquí está la parte que más me cambió la cabeza. Un api_backend pesa type_safety y observability alto, porque un endpoint que falla en runtime sin logs estructurados es deuda inmediata para quien esté on-call. Un library pesa feature_extensibility y api_migration_cost alto, porque una librería que rompe contrato con cada versión menor pierde adoptantes en semanas. Un cli_tool pesa team_onboarding y debuggability — alguien tiene que poder ejecutarlo sin manual, y cuando falla tiene que decir por qué.

Un react_spa mueve los pesos hacia accessibility y performance — porque ahí el costo de fallar es invisibilidad real para usuarios reales. Un script baja casi todo menos debuggability y team_onboarding — un script suele ser puente, no destino. Cada arquetipo es una opinión condensada sobre qué importa cuando el sistema entre en producción.

La observación interesante es que el mismo LLM, con el mismo prompt base, produce código distinto cuando los pesos cambian. No marginalmente distinto — distinto en estructura. Con type_safety alto agrega protocolos, validaciones de entrada, type guards. Con team_onboarding alto agrega docstrings, nombres más largos, comentarios explicando el por qué. Con observability alto envuelve operaciones críticas en spans nombrados. Esto está verificado en los benchmarks documentados en paper §3.

Es la diferencia entre decirle al modelo "hazlo bien" y decirle "estos son los ejes que importan, en este orden, con estos pesos". El primer caso es una invocación. El segundo es un contrato. La distancia entre invocación y contrato es la misma distancia que hay entre confiar en que el equipo entienda "qué queremos" y escribir un PRD: en algún momento, si quieres calidad consistente, tienes que pagar el costo de hacer explícito lo implícito.

Evaluation flowstatic + agent-native, scored per dimension code generated archetype output CodeEvaluator static checks AgentNativeEvaluator criteria to client LLM, no API key aggregate 13-dim radar, NOT a single number output is a vector, not a score

Cómo se evalúa

La evaluación pasa por dos caminos complementarios. El CodeEvaluator corre chequeos binarios estáticos — cosas que se pueden verificar sin un LLM: hay anotaciones de tipo, hay tests, hay handler de errores, el cliente HTTP tiene timeout, el endpoint expone health check, los logs son JSON. Son las dimensiones donde un AST o un linter alcanzan. La ventaja es velocidad y costo cero por evaluación.

El AgentNativeEvaluator es la pieza interesante. No llama a un LLM por sí mismo — devuelve los criterios al LLM cliente que ya está en la conversación. Esto significa que el evaluador funciona sin API key propia, porque el modelo del cliente hace el juicio. La ventaja operacional es doble: el usuario no paga dos veces por inferencia, y la herramienta no necesita gestionar credenciales de terceros. Para algunas organizaciones, lo segundo es la diferencia entre poder adoptar y no.

El output no es un número único. Es un score por dimensión, agregado al final en un reporte. Esto importa porque un score único colapsa información que el desarrollador necesita ver. Un módulo que saca 0.6 promedio puede tener type_safety en 0.95 y testability en 0.2 — el promedio no te dice qué arreglar. El reporte por dimensión sí.

La salida también incluye señales contextuales: qué archivos están bajos en qué dimensión, qué criterios específicos no se cumplen, qué cambios concretos subirían el score. Esto último es la parte que vuelve la herramienta útil dentro de un loop de iteración con un agente — el agente lee el reporte y sabe qué editar en la siguiente vuelta. El Demo #07 del canal de RLABS, sobre el note-api en arquitectura hexagonal que está en examples/note-api del repo agentguard-demo, muestra exactamente ese loop en acción.

La estructura del reporte importa tanto como el contenido. Un reporte que dice "observability: 0.4" es información. Un reporte que dice "observability: 0.4 — services/payment.py:43 ejecuta operación crítica sin span; routes/checkout.py:88 registra sólo el error sin correlation ID" es accionable. La diferencia entre información y acción es exactamente el trabajo que el reporte hace por el desarrollador, y ese trabajo es lo que justifica la existencia de la herramienta sobre, digamos, sólo correr un linter.

Lo que esto no resuelve

Las 13 son mías. Son razonables, no son canónicas. Si alguien intenta usarlas tal cual para un dominio que no se parece a lo que yo construí, va a forzar el ajuste y va a obtener una rúbrica que opina sobre cosas que no importan en su contexto. Mejor copiar el patrón — "decompone calidad en dimensiones explícitas con pesos por arquetipo" — que copiar la lista exacta. La lista exacta es accidente histórico de los proyectos que pasé por la mano.

La alucinación del evaluador sigue ahí. Cuando el AgentNativeEvaluator devuelve criterios al LLM cliente, el LLM cliente puede equivocarse en su autoevaluación. Puede decir que cumple un criterio que no cumple. Puede inventar evidencia. Mitigarlo es trabajo aparte — hay rúbricas más estrictas, hay self_challenge explícito que obliga al modelo a buscar contraejemplos, pero ninguna técnica mata el problema. Sólo lo acota.

Y esto no reemplaza la revisión humana de decisiones de producto. "¿Este endpoint debería existir?" no es una pregunta que respondan las 13 dimensiones. "¿El diseño de este flujo es el correcto para el usuario?" tampoco. Las 13 te dicen qué tan bien está hecho lo que decidiste hacer. La decisión de qué hacer sigue siendo tuya, y mejor que siga siéndolo.

Transferable Principle

Antes de medir "calidad", descomponla en dimensiones que tu equipo entienda. La lista exacta importa menos de lo que parece — lo crítico es que exista y que esté escrita en algún lado donde el modelo (y la persona que revisa) la puedan leer. Una rúbrica imperfecta y explícita le gana siempre a una rúbrica perfecta que vive en la cabeza de un único senior.

Los pesos importan más que las dimensiones. Los pesos son el contrato con tu negocio: dicen qué estás priorizando hoy, en este arquetipo, para este tipo de trabajo. Cambiar los pesos es una decisión arquitectónica, no cosmética. Cuando cambias los pesos de un arquetipo, estás diciendo que el tipo de proyectos que produce cambió de prioridades — y eso es información estratégica.

Si no puedes ponderar, no estás priorizando — estás esperando a que alguien revise y diga algo. Y "alguien revise y diga algo" no escala cuando el código que entra a revisión lo generó un agente que produce el equivalente a dos sprints de código por hora.

Pregunta para comentarios: si tuvieras que elegir las 3 dimensiones que más importan en tu stack hoy, ¿cuáles serían — y cuál estás midiendo de verdad?

#AI #GenAI #CodeQuality #Engineering #TechLeadership

Escríbenos por WhatsApp