La insoportable levedad de la codificación

Hace un mes, construí un sistema de recuperación completo con incrustaciones, búsqueda híbrida y una GUI en aproximadamente 25 horas. El fin de semana pasado, pasé dos días intentando corregir un error y me di cuenta de que no tenía idea de cómo funcionaba mi propio software.

Seamos honestos: publiqué un repositorio de GitHub sin haber escrito una sola línea de código. ¿Me siento mal por eso? Un poco. La cantidad de dudas técnicas pesa mucho sobre mis hombros, mucho más de lo que estoy acostumbrado. ¿Me arrepentiré? Tal vez. ¿Quieres?

Quería compartir mi historia aquí porque creo que esto es algo por lo que muchos desarrolladores están pasando en este momento, y muchos más lo experimentarán en los próximos años.

Porque seamos realistas: puedes tener un código de honor y estar orgulloso de tu destreza, pero nada supera la velocidad de GitHub Copilot & Co. Si tu colega con esteroides de IA envía funciones y publica actualizaciones dos veces (muy subestimado) más rápido que tú, ¿quién crees que está más cerca de la puerta de la empresa cuando los presupuestos se ajustan?

Las ganancias de productividad son reales, incluso si sólo utiliza estas herramientas para la documentación. Y hay un pequeño paso de:

“Escriba cadenas de documentación para esta función”.

a

“Escribe la función”.

Ese pequeño paso te lleva a un ámbito de productividad completamente diferente.

Pero aquí viene mi historia muy personal, lo que aprendí y dónde creo que esto nos deja como desarrolladores.

El proyecto: construir mi propio NotebookLM (pero más estricto)

Como antecedente, me propuse construir un sistema de recuperación de texto estilo RAG en el espíritu de NotebookLM, pero más estricto. El sistema toma una biblioteca de PDF privada, la procesa y luego recupera las respuestas palabra por palabra de ese corpus. Sin paráfrasis, sin frases alucinadas, simplemente “dame el pasaje exacto que responde a mi pregunta para que pueda buscarlo en el PDF original nuevamente”.

Es cierto que ésta es una forma muy científica y ligeramente paranoica de utilizar su literatura. Pero probablemente no soy el único que está cansado de cotejar cada respuesta de LLM con la fuente.

La arquitectura del software era bastante sencilla:

Un canal de ingesta sólido: recorrer árboles de directorios, extraer texto de archivos PDF y normalizarlo en párrafos y fragmentos superpuestos. Almacenamiento y recuperación híbridos: una capa de almacenamiento que combina tablas SQL estándar, un motor de búsqueda de texto completo de índice invertido (para coincidencias exactas de palabras clave) y una base de datos vectorial (para comprensión semántica). Una estrategia de reclasificación: algo de lógica para extraer un amplio grupo de candidatos mediante una búsqueda léxica y luego reclasificar los resultados utilizando una densa similitud de vectores para obtener lo mejor de ambos mundos. Una interfaz de usuario completa: un panel para administrar la biblioteca de PDF, monitorear el progreso de la ingesta y mostrar resultados con enlaces profundos al texto fuente.

Sobre el papel, todo esto es bastante sencillo. Python, Streamlit, SQLite+FTS5, FAISS, un modelo transformador de oraciones, todo envuelto en un contenedor Docker. No hay dependencias exóticas de la nube, solo una herramienta privada tipo NotebookLM ejecutándose en mi máquina.

El enfoque de la documentación primero

No comencé con el código, sino con la documentación. Ya tenía el esqueleto de mi proyecto habitual a partir de una plantilla sencilla, por lo que la estructura estaba ahí: un lugar para los requisitos, para las decisiones de diseño, para cómo implementar y probar, todo perfectamente ubicado en una carpeta de documentos esperando a ser llenado.

Escribí el caso de uso, esbocé la arquitectura, los algoritmos a implementar, los requisitos. Describí los objetivos, las limitaciones y los componentes principales en un par de viñetas y luego dejé que genAI me ayudara a ampliar las secciones más largas una vez que tuve una idea aproximada. Por lo tanto, pasé gradualmente de una idea básica a completar documentos más detallados que describían la herramienta. El resultado no fue la mejor documentación jamás obtenida, pero fue lo suficientemente claro que, en teoría, podría haber entregado el paquete completo a un desarrollador junior y él habría sabido qué construir.

Liberar a mi compañero de trabajo de IA al código base

En cambio, se lo entregué a la máquina.

Abrí las puertas y dejé que mi colega GitHub Copilot entrara al código base. Le pedí que creara una estructura de proyecto como mejor le pareciera y que completara los archivos de script necesarios. Una vez que se estableció una estructura básica y la herramienta pareció funcionar con un algoritmo, también le pedí que generara el conjunto de pruebas pytest, ejecutara la prueba e iterara una vez que encontrara algún error. Una vez hecho esto, seguí pidiéndole que implementara más algoritmos y cubriera algunos casos extremos.

En esencia, seguí mi enfoque habitual para el desarrollo de software: comenzar con un núcleo funcional, luego ampliarlo con características adicionales y arreglar las cosas cada vez que la construcción en crecimiento tiene problemas importantes. ¿Es esta una arquitectura globalmente óptima? Probablemente no. Pero está muy en el espíritu del Programador Pragmático: mantener las cosas simples, iterar y “enviar” con frecuencia, incluso si el envío es solo interno y solo para mí.

Y hay algo profundamente satisfactorio en ver cómo tus ideas se materializan en una herramienta de trabajo en un día. Trabajar con mi compañero de trabajo de IA fue como ser el líder de proyecto que siempre quise ser: incluso mis deseos a medias fueron anticipados e implementados en segundos como código prácticamente funcional.

Cuando el código no funcionaba, copié y pegué el seguimiento de la pila en el chat y dejé que el agente se depurara solo. Si se quedaba atascado en una madriguera de conejo autoinducida, cambiaba los modelos de GPT5 a Grok o viceversa y se depuraban entre sí como hermanos rivales.

Seguir su proceso de pensamiento y ver crecer el código base tan rápidamente fue fascinante. Solo mantuve una estimación de tiempo muy aproximada para este proyecto, ya que se trataba de un experimento paralelo, pero ciertamente no fueron más de 25 horas para producir >5000 líneas de código. Lo que sin duda es un gran logro para una herramienta relativamente compleja que, de otro modo, me habría ocupado durante varios meses. Todavía está lejos de ser perfecto, pero hace lo que pretendía: puedo experimentar con diferentes modelos y algoritmos de resumen además de un núcleo de recuperación que devuelve respuestas textuales de mi propia biblioteca, junto con la fuente exacta, para poder saltar directamente al documento subyacente.

Y luego lo dejé solo durante un mes.

La resaca de la deuda técnica

Cuando regresé, no quería agregar una característica importante. Solo quería contener la aplicación en Docker para poder compartirla con un amigo.

En mi cabeza, esta era una tarea ordenada del sábado por la mañana. En cambio, se convirtió en una pesadilla de fin de semana de tiempo completo con problemas de configuración de Docker, rutas que no se resuelven correctamente dentro del contenedor, cachés incrustados e índices FAISS que viven en lugares que no había separado claramente del código y pruebas que se pasan en mi máquina local pero fallan (o nunca se ejecutan correctamente) dentro de CI/CD.

Algunas de estas cuestiones dependen enteramente de mí. Felizmente asumí que mi canal de CI/CD (también generado por IA) se “encargaría de ello” ejecutando pruebas en GitHub, de modo que las inconsistencias entre plataformas aparecieran temprano. Spoiler: no lo hicieron.

cuando Copilot sugirió una solución aparentemente simple: “Simplemente agregue una referencia al directorio de trabajo aquí”. En lugar de dejar que tocara el código, quería mantener el control y solo pedir direcciones. No quería que causara estragos en un código base que no había visto durante semanas.

Fue entonces cuando me di cuenta de cuánto había subcontratado.

No sólo no me di cuenta de por qué ocurrió el error en primer lugar, sino que no pude identificar ni el archivo ni el pasaje en el que se suponía que debía realizar el cambio. No tenía idea de lo que estaba pasando.

Compare eso con otro proyecto que hice con un colega hace tres años. Todavía recuerdo cómo ciertas funciones estaban entrelazadas y el estúpido error que pasamos horas buscando, solo para descubrir que uno de nosotros había escrito mal el nombre de un objeto.

La verdad incómoda

Ahorré un enorme tiempo de desarrollo al saltarme el trabajo de implementación de bajo nivel. Mantuve el control de la arquitectura, los objetivos y las decisiones de diseño.

Pero no los detalles.

De hecho, me convertí en el líder tecnológico de un proyecto cuyo único desarrollador era una IA. El resultado se siente como algo que un contratista muy rápido y obstinado construyó para mí. El código tiene documentación inusualmente buena y pruebas decentes, pero sus modelos mentales nunca se me pasaron por la cabeza.

¿Podría arreglar algo si necesitara hacer un cambio y no tuviera Internet? Siendo realistas: no. O al menos no más rápido que si heredara este código base de un colega que dejó la empresa hace un año.

A pesar de la documentación mejor que la media, todavía me tropiezo con fragmentos de código “WTF”. Para ser justos, esto también sucede con el código escrito por humanos, incluido el mío de hace unos meses. Entonces, ¿GenAI está empeorando esto? ¿O simplemente más rápido?

Entonces… ¿la codificación de vibraciones es buena o mala?

Sinceramente: ambos.

La velocidad es una locura. El apalancamiento es real. La brecha de productividad entre las personas que utilizan estas herramientas agresivamente y las que no lo hacen no hará más que ampliarse. Pero está intercambiando intimidad de implementación por control arquitectónico.

Pasas de artesano a director de orquesta. De constructor a líder de proyecto. Desde conocer cada tornillo de la máquina hasta confiar en el robot que montó el coche. Y tal vez sea simplemente en eso en lo que se está convirtiendo silenciosamente la ingeniería de software.

Personalmente, ahora me siento mucho más como un líder de proyecto o un arquitecto principal: tengo el control del panorama general y estoy seguro de que podría retomar el proyecto en un año y extenderlo. Pero al mismo tiempo, no lo siento como “mi” código. De la misma manera que, en una configuración clásica, el arquitecto principal no es “dueño” de cada línea escrita por su equipo.

Es mi sistema, mi diseño, mi responsabilidad.

¿Pero el código? El código pertenece a la máquina.

Referencias