Resonant Coding: o cómo dejé de preocuparme y aprendí a amar el caos

Hay un fenómeno que ocurre cuando uno mira demasiado tiempo una pantalla con código que no entiende1. El cerebro empieza a hacer algo parecido a lo que hace cuando mirás nubes: busca patrones, caras, formas familiares. Solo que en vez de ver un conejo o el perfil de tu tía Marta, empezás a ver intenciones. Propósitos. Una lógica que seguramente está ahí, que tiene que estar ahí, porque alguien —o algo— lo escribió con algún objetivo en mente. El problema es cuando ese algo es un modelo de lenguaje y el objetivo era simplemente generar la secuencia de caracteres más probable dada una entrada, lo cual, si uno se detiene a pensarlo con la seriedad que merece, es una forma bastante perturbadora de crear las instrucciones que van a mover dinero, controlar sistemas, decidir cosas.

Esto no es una exageración paranoica2. O tal vez sí. Pero durante los meses que pasé liderando un equipo de desarrollo —un equipo nuevo, armado desde cero para dar soporte a un área que manejaba números con muchos ceros— esa paranoia se convirtió en algo parecido a una metodología, o al menos en algo que funcionaba mejor que no tener nada, que era exactamente lo que teníamos antes.

⸻ 1/8 ⸻

El contexto, porque el contexto siempre importa aunque uno preferiría que no: había presión desde arriba para usar Inteligencia Artificial y acelerar todo3. El equipo tenía pocos desarrolladores con experiencia, lo cual significaba que yo tenía que revisar casi todo. Y cuando digo “casi todo” me refiero a esa situación particular donde cada pull request4 que llegaba era como abrir una caja de Pandora manufacturada por un oráculo estadístico que había leído todo internet pero no necesariamente había entendido nada de lo que leyó5.

Los primeros meses fueron un desastre en cámara lenta. Código que a primera vista parecía razonable pero que, al examinarlo, revelaba estructuras que ningún programador humano hubiera elegido. Soluciones que funcionaban pero por las razones equivocadas. Patrones que olían a algo, pero no sabías a qué hasta que explotaban en producción en el peor momento posible, momento en el cual descubrías que el modelo había interpretado “validar la entrada del usuario” como “no tocar nada y esperar que no explote”.

Hubo un momento —y esto es importante para entender por qué eventualmente dejé de confiar en las soluciones mágicas— donde la calidad del código que entregaba el equipo mejoró de forma drástica. Por aproximadamente cuarenta y ocho horas gloriosas pensé que habíamos logrado algo. Que el método que veníamos refinando estaba funcionando. Que éramos, finalmente, buenos en esto.

Después descubrí que Cursor6 se había actualizado.

No éramos nosotros. Era el modelo. Y si el modelo podía mejorar sin nuestra intervención, también podía empeorar. O cambiar de formas que no entendíamos. O ser reemplazado por otro modelo con otras idiosincrasias que tendríamos que aprender desde cero. Estábamos construyendo sobre arenas movedizas y celebrando cada vez que las arenas, por un momento, dejaban de moverse7.

⸻ 2/8 ⸻

Tengo que hacer un desvío acá porque sin este desvío nada de lo que sigue va a tener sentido, y además es el tipo de desvío que disfruto porque involucra ondas, y las ondas son hermosas de una manera que el código rara vez logra ser8.

Hace unos años, una noche, en uno de esos pozos de YouTube donde uno termina sin saber cómo9, encontré un video de una demostración de física10. Alguien agarra una soga —o una cuerda, o algo parecido— y la empieza a agitar. Al principio es puro caos. Movimiento sin forma, ondas que chocan entre sí, interferencia destructiva por todos lados. Pero si encontrás la frecuencia correcta, si agitás a exactamente el ritmo necesario, algo extraordinario pasa: el caos se ordena. Aparecen puntos que no se mueven —los nodos— y puntos que oscilan al máximo —los antinodos—. Ondas estacionarias. Patrones que se sostienen solos porque el sistema entró en resonancia.

Me quedé mirando ese video probablemente más veces de las que admitiría en público. La idea de que el caos contiene estructuras latentes esperando a que alguien encuentre la frecuencia correcta para revelarlas. Que el desorden no es ausencia de orden sino orden potencial buscando manifestarse11.

Y años después, mientras miraba otro pull request incomprensible y me preguntaba cómo carajo íbamos a salir de este pantano, me acordé de las ondas estacionarias. Y pensé: tal vez el problema no es la IA. Tal vez el problema es que estamos agitando la soga a cualquier frecuencia y esperando que aparezcan patrones. Para encontrar la frecuencia correcta, primero había que entender la soga.

⸻ 3/8 ⸻

Los modelos de lenguaje, para quien no haya tenido el placer de interactuar con ellos más allá del chat ocasional, son esencialmente máquinas de predicción de texto. Esto suena simple y en cierto sentido lo es: les das una secuencia de palabras y te devuelven la palabra más probable que sigue. Como el autocompletado del celular pero entrenado con una cantidad de datos que es difícil de conceptualizar sin recurrir a metáforas astronómicas12.

El problema es que esta simplicidad fundamental genera comportamientos emergentes que parecen inteligencia, que a veces funcionan como inteligencia, pero que no son inteligencia en ningún sentido que un filósofo de la mente encontraría satisfactorio13. Son simulacros de razonamiento construidos a partir de patrones estadísticos. Y esto tiene consecuencias prácticas muy concretas:

No tienen memoria. Cada vez que les hablás es como si fuera la primera vez. El modelo que te ayudó a resolver un bug hace cinco minutos no tiene idea de que existís, ni de que hubo un bug, ni de que lo resolvieron juntos. Lo que algunas herramientas llaman “memoria” es en realidad un truco: guardan parte de la conversación anterior y la pegan silenciosamente al principio de cada mensaje nuevo. Es memoria prostética, artificial, y tiene límites14.

Su atención es limitada. Tienen una ventana de contexto —la cantidad de texto que pueden procesar a la vez— y cuanto más larga es la entrada, peor funcionan. La mejor manera de pensar esto es imaginar que tenés un balde de agua y tenés que lavar platos15. Para un plato, el balde sobra. Para diez, el agua empieza a enturbiarse. Para cien, el agua está tan sucia que los últimos platos salen peor de lo que entraron. Y si en algún momento tirás algo grasoso al balde —información irrelevante, contexto que no viene al caso— el agua queda inutilizable para todo lo que sigue. Además, la degradación no es uniforme: las partes del principio y las partes del final del contexto reciben más atención que el medio, lo cual explica por qué a veces el modelo “olvida” instrucciones que le diste tres párrafos antes16.

Son probabilísticos. Para la misma entrada pueden dar salidas diferentes. Esto suena menor pero tiene implicaciones profundas: no podés confiar en que un resultado que funcionó una vez va a funcionar de nuevo. Cada interacción es un dado siendo tirado, y a veces sale siete y a veces sale una serpiente que te come el prompt17.

⸻ 4/8 ⸻

En algún momento del verano, durante unas vacaciones que pasé leyendo sobre IA en vez de descansando —porque aparentemente tengo una relación disfuncional con el tiempo libre—, encontré dos líneas de pensamiento que eventualmente convergieron en lo que ahora llamo Resonant Coding. Una venía de Steve Yegge, un programador que lleva décadas escribiendo sobre software con una mezcla de brillantez técnica y opiniones que oscilan entre lo visionario y lo deliberadamente provocador18. La otra venía de Dex Horthy y su concepto de Context Engineering, que es básicamente la idea de que el contexto que le das a un modelo importa más que cualquier otra cosa19.

De Yegge tomé algo que él llama la Regla de los 5, que no es tanto una regla como un proceso iterativo de refinamiento. La versión simplificada es: cuando generás algo, lo pasás por cinco filtros sucesivos. Primero un borrador donde lo importante es que esté todo, aunque esté desprolijo. Después una revisión de exactitud donde arreglás errores factuales. Después claridad, donde simplificás y eliminás ambigüedades. Después casos límite, donde pensás en todo lo que podría salir mal. Y finalmente excelencia, donde pulís y optimizás20.

De Horthy tomé la obsesión por el contexto. La formalización de lo que ya intuía con la metáfora del balde: que la calidad de lo que le das al modelo determina la calidad de lo que te devuelve, y que si el problema es demasiado grande para un solo balde, tenés que dividirlo en partes más pequeñas y usar agua limpia para cada una.

Estos dos conjuntos de ideas, combinados con la desesperación profesional que ya mencioné y una cantidad moderada de cafeína, cristalizaron en algo que parecía funcionar. No inventamos nada nuevo —simplemente pegamos dos ideas que ya existían y les pusimos un nombre pretencioso—, pero a veces la innovación es exactamente eso: ver que dos piezas encajan cuando nadie las había puesto juntas.

⸻ 5/8 ⸻

No voy a describir el método como una serie de pasos numerados porque eso sería traicionar el espíritu de cómo realmente funciona, que es más caótico, más iterativo, más parecido a una espiral que a una escalera. Pero hay tres movimientos generales que se repiten:

Primero está lo que podríamos llamar investigación —aunque “reconocimiento del terreno” captura mejor la sensación. Antes de hacer cualquier cosa, hay que entender el problema. Y acá es donde el modelo puede ayudar: le pedís que investigue el código existente, que mapee dependencias, que encuentre documentación relevante, que te cuente qué mierda está pasando en este sistema que heredaste de alguien que ya no trabaja acá21. El modelo hace este trabajo bastante bien porque es esencialmente lectura y síntesis, que es exactamente para lo que fue entrenado.

Pero —y este pero es crucial— el documento que genera el modelo tiene que ser revisado. No aceptado, revisado. Con la Regla de los 5 o con algo parecido, no importa, pero con la convicción firme de que el modelo pudo haber entendido mal, pudo haber inventado cosas, pudo haber mezclado información de diferentes proyectos porque en su entrenamiento leyó código parecido y se le cruzaron los cables22. La revisión humana acá no es opcional; es el punto entero del ejercicio.

Después viene algo parecido a planificación, que es usar el documento de investigación (ya revisado) para generar un plan de acción. El truco acá es que cada tarea del plan tiene que ser lo suficientemente pequeña como para caber en un solo balde. Si una tarea es “implementar el sistema de autenticación”, es demasiado grande. Si es “agregar la validación del token JWT en el endpoint de login”, estamos mejor23. Y cada una de estas tareas pequeñas pasa, de nuevo, por la Regla de los 5, porque un plan mal definido puede generar miles de líneas de código incorrecto y para ese punto ya es tarde24.

Y finalmente está la implementación, que para este punto debería ser casi mecánica. Cada tarea está tan bien definida que el modelo no tiene espacio para inventar. Y acá es donde los modelos brillan de verdad: pueden editar veinte archivos en segundos, pueden crear baterías de tests, pueden refactorizar estructuras completas. Lo que a un humano le llevaría horas. Pero solo porque el trabajo difícil —el de pensar— ya se hizo antes25.

⸻ 6/8 ⸻

Sé lo que parece: otro método más que promete eficiencia. Pero la narrativa dominante sobre la IA en programación me molesta profundamente: la idea de que estas herramientas te permiten “ir más rápido”. No es falso, exactamente, pero tampoco es verdad de la forma en que usualmente se presenta. Es como decir que un auto te permite llegar más rápido a destino: cierto, pero solo si sabés manejar, si conocés el camino, si el auto está en buen estado, si las rutas están despejadas. Si no se cumplen esas condiciones, el auto te puede llevar muy rápidamente a cualquier lado, incluyendo un barranco26.

Con la IA pasa lo mismo. Sí, puede generar código más rápido de lo que cualquier humano podría escribirlo. Pero generar código no es lo mismo que resolver problemas. Y si generás código sin entender el problema, lo que obtenés es una solución rápida a la pregunta equivocada, que es peor que no tener solución porque ahora tenés que deshacer lo que hiciste antes de poder avanzar.

El método que describo no es un atajo. Es un proceso que toma más tiempo que tirarle un prompt al modelo y esperar que salga algo bueno. Pero ese tiempo se recupera multiplicado porque los errores se detectan temprano, porque el trabajo no tiene que rehacerse, porque cuando algo se implementa ya se sabe que es lo correcto27.

⸻ 7/8 ⸻

El método ya tenía forma. Faltaba el nombre.

Durante un tiempo dudé del nombre. “Resonant Coding” suena pretencioso, lo sé. Hubo versiones alternativas: Structured Prompting, que era demasiado genérico; Context-First Development, que sonaba a consultora de management; “El Método del Balde”, que era demasiado literal y además nadie iba a tomar en serio algo que se llama así28.

Pero seguía volviendo a la imagen de las ondas estacionarias. A la idea de que hay una frecuencia correcta esperando a ser encontrada. A la diferencia entre agitar la soga caóticamente y agitarla con precisión.

El Vibe Coding que se popularizó29 propone algo así como dejarse llevar, confiar en el modelo, iterar hasta que algo funcione. No digo que no sirva —hay situaciones donde esa aproximación es perfectamente válida— pero para trabajo serio, para sistemas que tienen que funcionar, para código que va a ser mantenido por otros humanos, necesitás algo más riguroso. Necesitás encontrar la resonancia30.

⸻ 8/8 ⸻

Debería probablemente cerrar con alguna conclusión elegante pero la verdad es que no tengo una. Lo que tengo es un proceso que funciona mejor que no tener proceso, que se puede refinar, que genera artefactos reutilizables31, que obliga a pensar antes de actuar. No es perfecto. Hay días donde todo falla igual. Hay modelos que se resisten a cooperar. Hay problemas que son genuinamente difíciles y ningún método los hace fáciles.

Pero cuando funciona —cuando el contexto está bien construido, cuando el plan está bien definido, cuando cada tarea cabe en su balde y el agua está limpia— hay un momento donde todo hace clic. Donde el modelo hace exactamente lo que necesitás que haga. Donde el código que genera es el código que habrías escrito vos, solo que más rápido y probablemente con menos errores. En esos momentos uno entiende, visceralmente, lo que significa encontrar la frecuencia correcta. Y después, invariablemente, llega el siguiente pull request incomprensible, y hay que empezar de nuevo.

Hay una pregunta que me persigue últimamente, una que no tiene respuesta fácil y que prefiero dejar resonando en vez de cerrar con una conclusión falsa: ¿qué pasa con los que vienen después? No hablo solo de los programadores —aunque también— sino de toda una generación que va a entrar al mercado laboral cuando estas herramientas sean ubicuas32. Los call centers ya están desapareciendo. El otro día me llamó el banco y me atendió una voz que sonaba humana pero era un robot. La conversación fue más fluida que la mayoría de las que tuve con humanos en ese contexto33. Los trabajos de entrada, esos que servían para aprender el oficio equivocándose en cosas que no importaban demasiado, están evaporándose. Y mientras tanto seguimos formando gente como si el mundo de mañana fuera igual al de ayer. Pero no son solo los jóvenes los que sufren esta transición.

No hay nada más cercano a mi imagen personal del infierno que ir a un banco y esperar horas viendo jubilados pidiendo ayuda para entender cómo sacar su propio dinero. Alguien del banco —con esa cara de quien ya perdió toda esperanza— los guía hacia una pantalla que bien podría ser un cartel dibujado con crayones por lo bien que funciona. O les dicen que usen el celular, sabiendo que sus dedos no pueden pegarle a las letritas ni navegar las laberínticas aplicaciones donde para ver tu saldo tenés que ser Indiana Jones de la tecnología34. Y acá hay algo que me da una pizca de optimismo: tal vez, solo tal vez, la IA pueda usarse para diseñar interfaces que contemplen a todo el mundo. Que se adapten. Que guíen. Que sean sencillas de verdad, no sencillas-para-el-que-las-diseñó.

Pero todo esto tiene un costo, y no hablo solo del costo humano35. Cada prompt, cada iteración, cada balde de agua limpia que usamos consume tokens, y los tokens cuestan dinero36. Es fácil olvidarlo cuando la herramienta funciona bien, pero hay una economía nueva emergiendo acá, una donde el recurso escaso no es el tiempo de máquina sino la capacidad de contexto, y donde ser estratégico en cómo usás tus tokens puede ser la diferencia entre un proyecto viable y uno que se come el presupuesto antes de entregar nada.

¿Qué pasa cuando se acaban? ¿Cuando el modelo que usabas sube de precio o desaparece? ¿Qué habilidades estamos dejando de desarrollar porque es más fácil pedirle al modelo que las simule?37 ¿Nos volvemos más eficientes o simplemente más dependientes?38

No tengo respuestas. Sospecho que nadie las tiene todavía. Pero me parece que estas son las preguntas que deberíamos estar haciéndonos ahora, mientras todavía hay tiempo de influir en cómo se responden.


Para una guía práctica del método, ver Resonant Coding: Guía práctica.


Notas