Yeison Daza
5 min de lectura

Haz difícil para los humanos escribir código en tu proyecto

La semana pasada se hizo viral un video de @poteto contando cómo llegó a cerrar 2K PRs al mes. Más allá del número, me dejó pensando en algo: antes de trabajar así, los equipos tienen que aprender a confiar en lo que producen sus agentes.

Este es el primero de una serie de textos sobre lo que estoy aprendiendo mientras intento hacerlo posible.

Antes las reglas eran fricción

En uno de mis primeros trabajos, antes de la IA, buena parte de las revisiones eran conversaciones sobre estilo: cómo escribir una condición, dónde poner una coma o cuál era la forma correcta de estructurar una función.

En ese momento Prettier empezaba a ganar tracción y propuse adoptarlo. Todavía recuerdo una conversación sobre si, al hacerlo, las personas perderían su toque al escribir código.

Me pasó muchas veces. Cada vez que quise añadir una regla de análisis estático o una validación, aparecía resistencia. Y lo entiendo: cuando escribías todo a mano, más reglas y una base de código existente por arreglar se sentían como más trabajo y menos autonomía.

Ahora las reglas son parte del entorno

Escribir código con IA ya hace parte del día a día de muchos equipos. Para que los agentes sean útiles, necesitan proyectos con validaciones estrictas.

Durante las últimas semanas he estado haciendo más estrictos los contratos del proyecto en el que trabajo. No para hacer más difícil contribuir, sino para dejar claro qué significa contribuir bien.

Estas son algunas de las cosas que hemos añadido.

Más reglas

Lo primero fue aumentar la cantidad de cosas que el proyecto puede detectar por sí solo.

En el backend, un proyecto en Python, usamos Ruff y pasamos de unas 100 a 400 reglas activas. En el frontend usamos oxlint y React Doctor.

El número de reglas no es lo importante. Lo importante es convertir los errores frecuentes y las convenciones del equipo en comprobaciones automáticas. Un agente puede escribir código que funciona y, aun así, dejar imports sin usar, duplicar lógica o seguir un patrón que el proyecto ya decidió evitar.

Cada regla le da al agente una respuesta concreta sobre qué tiene que corregir. En vez de esperar a que alguien se lo diga en una revisión, puede ejecutar las validaciones, arreglar el problema y volver a intentarlo.

Dependencias con límites

Por defecto, en un proyecto todo puede importar de todo. Parece flexible, pero termina siendo una trampa: una capa conoce detalles de otra, aparecen acoplamientos difíciles de deshacer y deja de estar clara la dirección de las dependencias.

Por eso añadimos reglas que controlan qué puede importar cada capa y cada paquete. Por ejemplo, los modelos no pueden importar vistas.

Para un agente, importar lo que tiene más cerca suele ser la solución más fácil. Puede hacer que el código funcione hoy, pero también introducir una dependencia que hará más difícil reutilizarlo, probarlo o cambiarlo mañana. Las reglas de importación protegen la arquitectura mientras se escribe el código.

Comprobación de tipos

La comprobación de tipos no solo encuentra errores. También le explica al agente qué datos está creando, recibiendo y transformando.

Cuanta más información tenga el sistema de tipos, menos espacio habrá para que el agente invente supuestos sobre una API, un modelo o el resultado de una función.

Límites

Una de las cosas que he aprendido con los años es que lo que puede crecer sin límite suele acabar siendo un problema. Eso también aplica al código.

Un archivo sin límite de tamaño, una función sin límite de complejidad o un flujo con demasiada anidación terminan siendo difíciles de entender y modificar. Podemos hacer que esos límites sean errores, no solo sugerencias.

En este proyecto usamos radon para medir complejidad. No porque un número pueda reemplazar el juicio técnico, sino porque tener un límite nos dice cuándo debemos parar y dividir un problema.

Los agentes encuentran los huecos

No basta con definir contratos: hay que validarlos y hacerlos cada vez más estrictos. Los agentes tienden a buscar el camino más fácil y terminan encontrando los huecos de las restricciones.

Por ejemplo, cuando habilité las reglas de importación, el agente empezó a crear archivos en la raíz de cada paquete para saltárselas. La solución no fue repetirle que no lo hiciera, sino añadir una validación que impidiera crear nuevos archivos ahí.

Y cuando una validación falla, no debería limitarse a decir «falló». Tiene que explicar la regla y mostrar qué hacer después. Una buena validación enseña.

Las instrucciones ayudan, pero no son una garantía. Un contrato ejecutable sí puede serlo.

Que escribir a mano sea incómodo

Creo que un proyecto debería tener reglas tan estrictas que escribir código a mano pueda resultar incómodo para una persona.

No se trata de crear burocracia porque sí. El camino fácil debería ser el correcto: estilo consistente, complejidad acotada, dependencias válidas y prevención de errores comunes.

Para un agente, estas reglas no son una molestia. Son límites claros sobre cómo se escribe código en ese proyecto. Si una validación falla antes de hacer commit o en integración continua, el agente puede corregir el problema y volver a intentarlo. Una regla nueva no tiene por qué convertirse en una discusión de días.

A veces el agente atribuirá un fallo a código que ya estaba ahí. En esos casos las instrucciones ayudan a delimitar su responsabilidad, pero la validación sigue siendo la referencia.

Cómo adoptarlo sin detener el proyecto

En un proyecto mediano o grande, activar todas las reglas y arreglar el pasado de una vez puede ser inviable. En mi caso, había alrededor de 20K errores del analizador estático.

La estrategia no tiene que ser una migración perfecta:

  1. Evita que el problema crezca. El código nuevo tiene que cumplir las reglas nuevas desde ahora.
  2. Limpia poco a poco. Usa agentes para atacar la deuda existente en cambios pequeños y verificables.

El objetivo es llegar a un proyecto donde cualquiera pueda introducir código, pero donde existan contratos claros sobre estilo, arquitectura, complejidad y corrección.

No estamos quitándole el toque humano al código. Estamos dejando de gastar ese toque en recordar convenciones implícitas y corregir errores repetibles.

Así es como construimos un entorno en el que un agente puede ser realmente útil.

En el próximo post voy a hablar de cómo usamos mutation testing para comprobar que los tests realmente nos dan confianza: Los tests necesitan ganarse su lugar en mi codebase.