1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • k10s era una TUI de Kubernetes con soporte para GPU creada rápidamente con vibe-coding junto a Claude, pero después de agregar la vista de fleet se rompieron varios estados de pantalla
  • model.go creció hasta convertirse en un único Model de 1690 líneas con un Update() de 500 líneas, cargando con todo el estado de UI, cliente, caché, navegación y vistas
  • La IA agregó funciones rápido, pero también hizo crecer un god object y un key handler global, dejando una estructura donde cada vista nueva sumaba más branches al handler existente
  • Los datos posicionales en []string y la mutación directa desde tea.Cmd en background podían provocar errores de columnas y una data race evidente
  • El nuevo k10s se reescribirá en Rust, y antes del primer prompt se fijarán en CLAUDE.md la interfaz, los tipos de mensaje, las reglas de ownership y el alcance

Por qué terminé reescribiendo k10s

  • k10s comenzó como un dashboard de Kubernetes con soporte para GPU, una herramienta TUI pensada para que operadores de clústeres NVIDIA pudieran ver de inmediato información como uso de GPU, métricas DCGM, nodos inactivos y costos de $32/hr
  • Fue escrito en Go y Bubble Tea, y se construyó durante unos 7 meses, 234 commits y cerca de 30 fines de semana en sesiones de vibe-coding con Claude
  • Al principio, funciones básicas tipo clon de k9s como pods, nodes, deployments, services, command palette, live updates basados en watch y Vim keybindings ya funcionaban en apenas 3 fines de semana
  • La función principal, la vista de GPU fleet, era una pantalla que mostraba la asignación de GPU por nodo, uso, métricas basadas en DCGM, temperatura, energía, memoria y estado con colores; Claude generó de una vez la estructura FleetView, el filtrado por pestañas GPU/CPU/All y hasta el render de allocation bars
  • Después de agregar la vista de fleet, al volver a la vista de pods con :rs pods, la tabla quedaba vacía, los live updates se detenían, en la vista de nodes aparecían datos stale del filtro de fleet y también se desajustaba el conteo de pestañas de fleet
  • Al rastrear el problema, terminé leyendo por primera vez las 1690 líneas completas de model.go generadas por Claude, donde una sola estructura Model cargaba widgets de UI, el cliente de Kubernetes, estado de logs/describe/fleet, historial de navegación, caché y manejo de mouse
  • El método Update() tenía unas 500 líneas y estaba armado como una función de dispatch de msg.(type) con 110 ramas de switch/case
  • La IA puede crear funciones rápido, pero si se le deja seguir sin restricciones, la arquitectura se derrumba, y esa sensación de velocidad parece éxito hasta el momento en que todo colapsa al mismo tiempo

Cinco principios que salieron de los restos

  • Principio 1: la IA crea funciones, pero no arquitectura

    • Claude hizo bien funciones individuales como la vista de fleet, log streaming y soporte de mouse, pero cada una fue implementada en el contexto de “hacer que funcione ahora”, sin considerar su relación con otras funciones que compartían el mismo estado
    • El handler de resourcesLoadedMsg terminó incluyendo condiciones como msg.gvr.Resource == k8s.ResourceNodes && m.fleetView != nil, mezclando lógica específica de la vista de fleet dentro del flujo genérico de carga de recursos
    • Cada vez que una vista nueva necesitaba comportamiento personalizado, se agregaba otra branch al mismo handler, y además había que limpiar a mano varios fields para que los datos de la vista anterior no se filtraran a la nueva
    • En model.go había 9 limpiezas manuales dispersas como m.logLines = nil, m.allResources = nil, m.resources = nil; si faltaba una sola, quedaban datos fantasma de la vista anterior
    • La alternativa es escribir antes del código las interfaces concretas, los tipos de mensaje y las reglas de ownership, e incluirlos en CLAUDE.md como invariantes de arquitectura
    • Un ejemplo de regla sería que cada vista implemente el trait/interface View, que una vista no acceda al estado de otra, que todos los datos async entren solo como variantes de AppMsg y que la struct App solo se encargue de la navegación y el dispatch de mensajes
  • Principio 2: el god object es el resultado por defecto que produce la IA

    • La IA se inclinó a una estructura donde una sola struct contiene todo, porque así puede satisfacer el prompt inmediato con la menor ceremonia posible
    • El manejo de teclas tampoco estaba separado por vista, y una misma tecla s significaba autoscroll en la vista de logs, shell en la vista de pods y container shell en la vista de containers
    • La solicitud de “agregar soporte de shell a pods” se implementó insertando otra branch cerca del key handler global ya existente
    • La tecla Enter también se bifurcaba entre lógica de contexts view, namespaces view, logs view y drill-down genérico, todo dentro de un único dispatch plano comparando strings de m.currentGVR.Resource
    • Dentro del único archivo model.go, m.currentGVR.Resource == aparecía más de 20 veces como discriminador de tipo, y agregar una vista nueva obligaba a tocar varios handlers
    • La alternativa es no agregar fields de estado específicos de vista en App/Model, crear cada vista como una struct separada y poner los key bindings en el keymap de la vista activa, dejando esa regla por escrito en CLAUDE.md
    • Hace falta un guardrail del tipo “agregar una vista debe equivaler a agregar un archivo; si requiere modificar una vista existente, detenerse y preguntar”, para que la IA no elija el camino más corto de sumar otra branch
  • Principio 3: la ilusión de velocidad expande el alcance

    • k10s empezó como una herramienta para un público acotado que opera clústeres de entrenamiento con GPU, pero el vibe-coding hizo que funciones como pods, deployments, services, command palette, soporte de mouse, contexts y namespaces se sintieran “gratis”
    • Como resultado, dejó de ser una herramienta enfocada en GPU y se fue ampliando hacia una TUI de propósito general para todos los usuarios de Kubernetes, básicamente rehaciendo k9s
    • En un keyMap plano se mezclaban bindings específicos de muchas vistas, como Fullscreen, Autoscroll, ToggleTime, WrapText, CopyLogs, ToggleLineNums, Describe, YamlView, Edit, Shell, FilterLogs, FleetTabNext y FleetTabPrev
    • Autoscroll y Shell usaban ambos la tecla s, y como el dispatch revisaba el recurso actual, “funcionaba”, pero ya no era posible entender localmente el keybinding
    • La velocidad para escribir código parecía “shipping”, pero cada feature agregaba el costo de una branch más dentro del god object
    • La alternativa es dejar claro en CLAUDE.md el límite de alcance: que k10s es para operadores de clústeres GPU, que las vistas soportadas se limitan a fleet, node-detail, gpu-detail y workload, y que no se agregarán vistas genéricas de recursos ni funciones duplicadas de k9s
    • La IA puede dar un presupuesto infinito de líneas, pero el presupuesto de complejidad sigue siendo finito, así que el alcance debe rechazarse por adelantado
  • Principio 4: los datos posicionales son una bomba de tiempo

    • k10s aplanaba directamente los recursos recibidos desde la API de Kubernetes a la forma type OrderedResourceFields []string
    • La función de ordenamiento de la vista de fleet trataba ra[3] como Alloc, ra[2] como Compute y ra[0] como Name, y la identidad de las columnas dependía únicamente de comentarios y del orden de columnas en resource.views.json
    • Si en resource.views.json se agregaba una columna entre Instance y Compute, el sort, el render condicional y el drill target que referenciaban ra[2] y ra[3] podían empezar a fallar silenciosamente
    • El compilador no puede saber qué significa un []string, y la config JSON tampoco puede expresar comportamiento de sort, render condicional o custom drill target, así que el código Go terminaba hardcodeando supuestos posicionales
    • La IA tiende a elegir []string o Vec<String> porque son fáciles de meter directo en un widget de tabla, mientras que una struct tipada exige más ceremonia inicial y por eso queda fuera del camino rápido
    • La alternativa es mantener datos estructurados como FleetNode o PodInfo hasta justo antes del render, y hacer el sort sobre fields con nombre en vez de accesos posicionales como row[3]
    • Una estructura de ejemplo sería FleetNode { name, instance_type, compute_class, alloc }, donde la identidad de las columnas queda expresada en el tipo y ya no es posible construir estados inválidos como ordenar por una columna equivocada
    • “Making impossible states impossible” es una expresión usada en las comunidades de Elm/Rust que significa diseñar tipos para que un estado inválido no pueda construirse, en vez de detectarlo con checks en runtime
  • Principio 5: la IA no debe ser dueña de las transiciones de estado

    • La idea central del modelo de Bubble Tea es que el estado solo cambia dentro de Update() impulsado por mensajes, pero k10s rompía esa regla
    • El handler de updateTableMsg devolvía un closure de tea.Cmd, y dentro de ese closure se modificaban fields del Model con llamadas como m.updateColumns(m.viewWidth), m.updateTableData() y m.table.SetCursor(savedCursor)
    • Bubble Tea ejecuta tea.Cmd en un goroutine separado, así que mientras ese closure leía y escribía m.resources, m.table y m.viewWidth, el View() del goroutine principal podía estar leyendo esos mismos fields
    • No había lock ni mutex, y <-m.updateTableChan solo esperaba una señal de actualización; no evitaba que View() leyera un estado escrito a medias
    • Esa estructura era una data race evidente, y normalmente se manifestaba como algo que “casi siempre funciona” pero ocasionalmente rompe el display
    • La alternativa es que el worker en background no mutile directamente el estado de UI, sino que envíe mensajes tipados por un channel y que el event loop principal aplique la mutación de estado al recibirlos
    • La regla de concurrencia es que una tarea de background no debe cambiar directamente el estado de UI, que debe enviar el resultado como mensaje tipado, y que render()/view() debe ser una función pura sin side effects, I/O ni operaciones sobre channels

Reglas de protección para CLAUDE.md y agents.md

  • Invariantes de arquitectura

    • Cada vista debe implementar el trait/interface View y no debe acceder al estado de otras vistas
    • Todos los datos async deben entrar como variantes de AppMsg, y ninguna tarea en background debe mutar fields directamente
    • Agregar una vista nueva no debe requerir modificar vistas existentes
    • La struct App debe ser un router delgado encargado solo de la navegación y del dispatch de mensajes
  • Reglas de ownership del estado

    • No se deben agregar fields de estado específicos de vista a la struct App/Model
    • Cada vista debe existir como una struct separada y declarar sus propios key bindings
    • La app debe despachar las teclas a la vista activa, y todo keybinding nuevo debe agregarse al keymap de esa vista, no al handler global
    • Si agregar una vista exige modificar una vista existente, hay que detenerse y pedir confirmación
  • Alcance

    • k10s debe ser una herramienta para operadores de clústeres GPU, no para todos los usuarios de Kubernetes
    • Las vistas soportadas deben limitarse a fleet, node-detail, gpu-detail y workload
    • No se deben agregar vistas genéricas de recursos como pods, deployments o services
    • No se deben agregar funciones que repliquen capacidades de k9s
    • Toda feature request que no ayude a operadores de trabajos de entrenamiento con GPU debe rechazarse
  • Representación de datos

    • No se deben aplanar datos estructurados a []string, Vec<String> ni arreglos posicionales
    • Los datos deben mantenerse en structs tipadas hasta justo antes de la llamada a render
    • La identidad de las columnas debe venir del nombre del field en la struct, no del índice del arreglo
    • Las funciones de sort deben operar sobre fields tipados, no sobre accesos posicionales como row[3]
    • La generación de strings para display debe ocurrir solo dentro de render()/view()
  • Reglas de concurrencia

    • Las tareas en background como watcher, scraper o API call no deben mutar directamente el estado de UI
    • Las tareas en background deben enviar sus resultados al channel como mensajes tipados
    • Solo el event loop principal debe aplicar mutaciones de estado a partir de los mensajes recibidos
    • render()/view() debe ser una función pura sin side effects, I/O ni operaciones sobre channels
    • Si hace falta cambiar el estado como resultado de trabajo async, debe definirse una nueva variante de AppMsg

Cómo lo están rehaciendo

  • k10s será reescrito en Rust, no porque Rust sea mejor, sino porque se siente como un lenguaje que el autor puede dirigir directamente
  • En un lenguaje que uno ha usado lo suficiente, puede detectar qué está mal incluso antes de explicarlo con palabras, y esa sensibilidad no puede ser reemplazada por el vibe-coding
  • Cuando la IA produce código que parece plausible, hace falta la capacidad de detectar si en realidad es basura
  • En la nueva versión, antes de escribir código, una persona hará primero a mano el trabajo de diseño, como interfaces concretas, tipos de mensaje y reglas de ownership
  • En lugar de dejar que la IA tome decisiones equivocadas de arquitectura, ahora esas decisiones se fijarán por escrito antes del primer prompt
  • Los enlaces al TUI original y al proyecto están en k10s Github y K10S.DEV

Nota adicional

  • Bubble Tea es un framework TUI de Go basado en The Elm Architecture, y los problemas de arquitectura de k10s no vienen de Bubble Tea sino de la implementación de k10s
  • “Making impossible states impossible” es una expresión de las comunidades Elm/Rust para referirse a diseñar tipos que impidan construir estados inválidos, en lugar de validarlos en runtime
  • Así como en la escritura con IA el “em-dash” puede quedar como un olor, en el código con IA el “god object” puede ser esa señal, y el vibe-coding puede hacer que implementar cosas se sienta demasiado barato, llevando a pérdida de foco y bloat

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Hacker News
  • Quienes dicen que el código generado es aceptable, por lo general, son personas que no leen ese código
    La mitigación propuesta en el artículo tampoco aguanta mucho tiempo. Al diseñar un sistema o componente surgen invariantes como “una vista no accede al estado de otra vista”, pero tarde o temprano hay que agregar una función que choca con esa condición
    En ese momento, normalmente hay que abandonar la función, montarla de forma torpe e ineficiente sobre el invariante, o cambiar el propio invariante. Esa elección no es solo una cuestión de contexto, sino de criterio, y los modelos actuales fallan demasiado seguido en ese juicio
    Si explicitas restricciones de arquitectura, el agente genera código complejo e inmantenible, forzado para encajar en esas restricciones incluso cuando habría que cambiarlas. Si no lo lees con más cuidado que el código escrito por personas, al final aparece “código que se devora a sí mismo” y te das cuenta demasiado tarde

    • Si sabes escribir buen código, puedes lograr que la IA también lo escriba usando varias técnicas; es totalmente posible
      La clave es identificar los puntos que a la IA le cuestan y hacérselos fáciles. Por ejemplo, se necesitan contextos extremadamente pequeños, modularización con límites claros, módulos puros separados de la entrada/salida, ocultar cosas detrás de interfaces, 100 pruebas que corran en menos de 1 segundo, benchmarks, etc.
      La IA funciona bien cuando hay límites y contexto pequeño. Si no se los das, su desempeño empeora, y la responsabilidad es de quien usa la herramienta
    • Creo que el hecho de que en algún momento terminas agregando una función que choca con un invariante es un gran problema del desarrollo guiado por especificaciones
      Ninguna especificación sobrevive a la realidad, y aunque investigues y diseñes lo suficiente, al final se revela que algún invariante dentro de esa especificación estaba mal
      Cuando un humano se encuentra con esto durante el desarrollo, puede dar un paso atrás y repensar si el invariante estaba mal y qué impacto tendría cambiarlo. En cambio, la IA muchas veces termina improvisando una solución hackeada bajo supuestos o diseños erróneos, y le falta la intuición para reevaluar el conjunto
      Puede mejorar con buenos flujos de trabajo y validación, pero no es un área que herramientas como Claude Code resuelvan bien por defecto, y ahí hay un límite
    • Me pasó algo parecido creando un nuevo framework interno en la empresa y migrando los usos del framework anterior
      Al principio establecimos principios fuertes y movimos a mano algunos casos de uso para ganar confianza. La migración completa era tan grande y costosa que se había postergado casi 10 años, así que intentamos acelerarla con IA para bajar el costo
      La IA estuvo bien para el 80% de los casos mecánicos y simples. El 20% restante requería cambios en el framework; la mayoría eran pequeños, como agregar campos a una API, pero uno o dos requerían una reconceptualización
      El backend de cierto sistema podía producir ciertos datos en el 99% de los casos, pero en algunos casos importantes lógicamente no podía y había que reportarlos desde afuera. Sin embargo, una optimización importante estaba construida sobre la suposición de que “eso es imposible”
      La herramienta de IA no detectó esta situación y agregó la lógica migrada como si fuera a funcionar correctamente. Gracias a la forma en que desplegamos, todavía no era un bug en producción, pero al hacer las preguntas correctas al equipo asociado descubrimos que la misma necesidad existía en otros lugares
      Al final, no se convirtió en un problema grave porque una persona estaba metida a fondo. En el futuro, herramientas de validación y modelos más inteligentes quizá faciliten más este tipo de migraciones, pero por ahora el código generado parece hermoso aunque esté roto, así que hay que vigilarlo de cerca todo el tiempo
    • No se trata solo de leer el código que sale; al menos en mi experiencia, también hay que escribir código uno mismo
      Tenía un patrón de arquitectura raro que llevaba unos dos meses usando, y cada vez me incomodaba un poco, pero no fue hasta anoche que me di cuenta de que no era una buena abstracción y de que había una mejor forma de dividirlo
      Cuando dejas que un LLM genere el código, esa incomodidad se siente mucho menos claramente, así que tardé más en detectar el problema y encontrar una solución. Está bien generar lo periférico, pero las funciones centrales todavía conviene escribirlas uno mismo en su mayoría
    • Los invariantes escritos de manera informal son difíciles de demostrar como rotos incluso con un revisor humano de por medio, y el lenguaje natural no es lo bastante preciso para esa tarea
      Incluso si los expresaras en un lenguaje formal preciso, al LLM que está debajo del agente le falta capacidad para entender por qué ese invariante es necesario y por qué importa. Puede que aparezca un LLM que conecte tokens con especificaciones formales y hasta escriba pruebas, pero seguirán saliendo códigos raros generados desde la parte informal del prompt
      No se puede evitar solo agregando restricciones y prompts a una lista técnica o a una especificación. Por mejor que hagas la trampa, el animal igual se escapa
      El problema es la inflación de código: se sigue agregando código para satisfacer el prompt o la tarea. Muchas veces menos código es mejor, y se necesita alguien que pueda anticipar qué quieren y qué esperan los demás. Los generadores son buenos, pero hay que usarlos con un poco más de moderación, como una manguera de bomberos
  • Cuando Copilot autocompletaba una sola línea, decían “igual tú tienes que escribir la función completa”; luego completó la función y dijeron “igual tú tienes que escribir la lógica alrededor”; luego completó también esa lógica y dijeron “igual tú tienes que escribir la funcionalidad”
    Ahora que ya completa la funcionalidad, dicen “igual tú tienes que hacer la arquitectura”. No sé si estos modelos pueden resolver la arquitectura, pero es interesante ver cómo la expectativa se sigue moviendo

    • Esas “personas” imaginarias estaban equivocadas desde el principio
      Aunque la IA complete una línea, una función entera, una funcionalidad o un ticket, igual tienes que leer y entender el código
    • Los modelos también pueden hacer arquitectura, pero por ahora suelen hacerlo muy mal a menos que los guíes con fuerza
      Uso IA todo el tiempo y va mejorando, pero igual reviso cada línea. Incluso a nivel de líneas individuales, hoy no diría que sea mejor que el autocompletado con tab del año pasado; a veces es muy bueno, pero otras veces realmente es muy malo
    • Creo que la solución está entre líneas en el artículo
      Los LLM son excelentes para desarrollar software, pero solo cuando no les dejas escribir la arquitectura. Los módulos, structs y enums hay que hacerlos uno mismo, y en lo posible también agregar uno mismo los campos y variantes
      Conviene poner comentarios de documentación en cada struct, enum, campo y módulo, y luego hacer que el LLM mire ese módulo y esas estructuras de datos para rellenar los cuerpos de funciones necesarios y cosas por el estilo
    • Creo que los lenguajes actuales no escalan bien porque los codebases son globalmente complejos y los invariantes deseados no quedan visibles
      Aunque le digas varias veces “nunca bloquees en la ruta crítica”, el LLM igual mete bloqueos en la ruta crítica; y aunque le digas “si haces X, hace falta una prueba de tipo Y”, hace X y omite la prueba
      Los humanos tampoco siguen instrucciones al 100%, pero el LLM es más aleatorio. Los errores humanos, relativamente, hacen menos veces exactamente lo opuesto de lo que uno quería
      El LLM puede ver invariantes importantes en el código y aun así crear atajos, escribir pruebas que hagan pasar un fallo como éxito, decir que hizo lo pedido y enterrarlo dentro de un commit de 5 mil líneas
      Estoy convencido de que los LLM son excelentes y son el futuro, y por eso estoy creando un lenguaje llamado https://GitHub.com/Cuzzo/clear para ellos. Hay que superar el problema de lenguajes que exigen contexto global donde no debería hacer falta, para que sea más fácil trabajar junto con ellos
      He tenido éxitos, pero ha sido tan frustrante que a veces me pregunto si valía la pena gastar tanta cordura en esto
    • A esto lo llamo arquitectura desechable
      No significa que la arquitectura no importe, sino que una arquitectura que funcionaba bien ayer no necesariamente tiene que seguir siendo la correcta hoy
  • Al usar agentes de programación, me puse algunas reglas
    Primero: si voy a generar código con un agente, tiene que ser algo de lo que esté absolutamente seguro de que yo mismo podría implementar bien si tuviera tiempo
    Segundo: si no es así, no avanzo hasta entender completamente lo generado y poder reproducirlo por mi cuenta
    Tercero: si rompo la segunda regla, puedo generar deuda cognitiva, pero tengo que pagarla por completo antes de declarar terminado el proyecto
    Cuanto más se acumula esa deuda, más probable es que baje la calidad del código generado después, y da la sensación de crecer con interés compuesto. En proyectos personales este enfoque es agradable, aprendo mucho y me queda un codebase que entiendo con comodidad

    • Es una regla razonable para mantener un modelo mental sólido sobre la cordura del código y el crecimiento del codebase, pero es difícil de seguir en el trabajo, donde tras la IA las expectativas de velocidad de entrega cambiaron muchísimo
      Hace falta encontrar un punto de equilibrio donde sigas conectado al codebase sin convertirte en el cuello de botella del equipo
    • Intenté seguir reglas parecidas, pero una vez me topé con un problema matemático difícil
      Claude es un matemático nivel doctorado y yo no, pero yo sí sabía exactamente qué propiedades quería de la solución y cómo probar si era correcta. Así que dejé la solución de Claude en vez de mi solución simple e ingenua, lo anoté en el pull request y todos estuvieron de acuerdo en que había sido la decisión correcta
      Me pregunto si en casos así debería haber una excepción. Si la IA llega a ser mucho mejor que yo no solo en matemáticas avanzadas sino también programando, la pregunta más interesante es si dejaría por completo de escribir código a mano, aunque eso implique perder mi capacidad de juzgar el código directamente, bajo la premisa de que sí podría seguir evaluando las pruebas
    • Me gusta más el término deuda de entendimiento que “deuda cognitiva”
      Es una expresión más precisa, porque la deuda que se acumula es exactamente falta de comprensión del código
    • En proyectos personales puede estar bien porque importa más cuál forma te resulta más disfrutable, pero en el trabajo no entiendes así de fondo todas las capas, desde dependencias, trabajo de colegas, servicios externos, hasta el silicio
      No sé por qué de repente con la IA tendría que tratarse distinto
      Al final hay que juzgarlo por riesgo y recompensa. Hay que evaluar cuál es la pérdida si sale mal, qué tan probable es detectarlo en pruebas y revisiones, y qué beneficio obtienes si sale bien. Lo mismo aplica a librerías y servicios externos
      Un contrato complejo de criptomonedas con reglas financieras imposibles de actualizar y sin pruebas no tiene para nada el mismo riesgo que un visor para visualizar datos internos de logs
    • Probé un enfoque parecido, pero al final creo que cumplir de verdad la segunda regla es poco realista
      En teoría suena bien, pero en la práctica siempre terminas tomando atajos mentales sin darte cuenta
      Si comparo arreglar un problema en un codebase desconocido por mi cuenta versus sentir que “entendí por completo” lo que hizo un agente, una semana después no me queda lo mismo en la cabeza. Si lo hago yo mismo, se acumula como conocimiento general y normalmente permanecen las partes importantes; pero si intento apropiarme como mío de lo que hizo el agente, en el momento parece que lo entendí y luego se me olvida muy rápido
      Por eso concluí que en estos casos la ayuda del LLM suele ir contra mis objetivos, incluso sin considerar otras preocupaciones como el tiempo o la presión del negocio
  • A mí me pasó exactamente lo mismo
    La estafa funciona así: en un buen codebase, la IA puede crear muchas funciones y hasta parece más rápida, más segura y más precisa. Sobre todo en áreas que uno no conoce bien, esa impresión es aún más fuerte
    Con el tiempo el codebase crece, lleva más tiempo navegarlo y la tasa de fallos sube. Como cuesta admitirlo, uno insiste más, hasta que se detiene recién cuando cambiar algo ya es casi imposible
    Cuando vuelves a mirar el código, “spaghetti” se queda corto: es más bien una Gran Muralla China
    Al final borré 75 mil líneas de 140 mil, y siento que esos tres meses de inmersión fuerte en agentic coding fueron una pérdida. Construí funciones inútiles, aumenté los bugs, perdí mi modelo mental del código, me salté decisiones difíciles que solo se ven cuando estás dentro del código, y también les fallé a los usuarios

    • Me parece interesante que ese resultado te haya sorprendido
      No lo digo con sarcasmo; de verdad me da curiosidad cuál era la expectativa inicial y de dónde salió
      Parece que con los LLM la gente tiene expectativas diferentes. Si le pasaras a un “desarrollador” cualquiera que solo conoces por internet una descripción resumida de una funcionalidad y te devolviera un montón de implementación medio rota, nadie se sorprendería
      Sin embargo, a veces la gente espera de una máquina que alucina largamente milagros que ni siquiera esperaría de un humano. Me pregunto de dónde sale esa confianza
    • Creo que un codebase grande debería ser una colección de codebases pequeños
      Igual que una gran ciudad es una colección de ciudades pequeñas, debe haber un mapa, y uno debería poder hacer zoom a un área local y trabajar dentro de ese alcance. No hace falta conocer todos los detalles de Nueva York para ir a tomar un café
      Crear una arquitectura sana y mantenible es responsabilidad de quien usa la herramienta. La IA no lo impide, y si usas bien la herramienta incluso puede ayudar
    • Seguramente hay soluciones de flujo de trabajo aparte de abandonar la IA
      Por ejemplo, tratar de inmediato el código generado por IA como código legado, poner fronteras de encapsulación fuertes e interfaces bien definidas, y luego integrarlo en un flujo más manual
      Hay todo un espectro, desde prompts aislados hasta generación de código en línea, y la forma adecuada cambia según el problema y la parte del codebase
      La generación aislada encaja más en la etapa de prototipo, cuando repites mucho la especificación; cuando el prototipo se asienta, puedes bajar a generación por módulo o por archivo para trabajar de forma más sistemática, manteniendo en ese nivel un buen modelo mental
    • Me pregunto si no leyeron el código generado y simplemente hicieron auto-commit de todo
      Si sí lo leyeron pero no lo entendieron, entonces podrían haber pedido comentarios detallados en cada salida; y si saben que al modelo le cuesta más a medida que crece el codebase, entonces cuanto más aumenta la complejidad, con más rigor hay que revisar la salida
    • No he trabajado con codebases grandes, pero me pregunto si no se podría aplicar un flujo al estilo de Working Effectively with Legacy Code
      Crear islas de código de mayor calidad, usar la IA para ayudar a reconstruir la intención del desarrollador y las reglas del negocio, y construir seams y pruebas unitarias alrededor del módulo objetivo
      La IA no necesariamente tiene que servir para aumentar throughput; también puede ser una herramienta flexible de exploración y refactorización que ayude al trabajo manual o a implementaciones posteriores con agentes
  • Cada vez que veo textos como este termino comparando la velocidad que la gente dice obtener con IA con la velocidad que yo obtengo simplemente programando a mano
    Casualmente llevo 7 meses trabajando en un proyecto de MMO 3D, y ya se puede jugar, a la gente le parece divertido, los gráficos están bien y puedo meter fácilmente cientos de personas en el servidor. La arquitectura también está bastante bien, así que es fácil extender funciones, y probablemente podría lanzarlo tras cerca de un año de desarrollo
    Mientras tanto, el autor original no pudo ni hacer una TUI básica en 7 meses de vibe coding. La velocidad de funcionalidades puede sentirse alta, pero para crear una UI básica como esa parece increíblemente lento. Hay muchas buenas librerías de TUI y es el tipo de cosa que se puede hacer a mano en unas semanas llenando una tabla con los datos necesarios
    Con IA la sensación de avanzar rápido y mucho es fuerte, pero en la práctica muchas veces parece bastante más lenta que programar manualmente. Los datos de productividad también parecen respaldar la idea de que los usuarios de IA sienten que van más rápido, pero en realidad producen menos

    • Esa métrica depende muchísimo de quién usa IA y para qué
      El mayor consumo de tiempo en el desarrollo de software son las reuniones para alinear expectativas de stakeholders y soluciones. Desde esa perspectiva, la IA casi no ayuda, así que si comparas horas-persona desde la propuesta hasta entrar al loop de pruebas, probablemente el resultado sea decepcionante
      Pero para resolver problemas, corregir bugs e implementar soluciones ya aprobadas, siento que es por lo menos 10 veces mejor que antes. No solo en tiempo puro, sino también en la capacidad para interpretar comportamiento observado e investigar problemas
      Eso sí, hay gente que no puede obtener resultados precisos y valiosos con IA. Si sabes exactamente qué quieres y cómo lo quieres, la IA ayuda muchísimo. Si le pides algo que yo igual iba a hacer, lo hace más rápido. Pero si no sabes con precisión lo que quieres, la IA perjudica el avance
    • Yo también llegué recientemente a la misma conclusión
      Cuando la gente muestra cosas hechas con LLM, rara vez impresionan, porque en su mayoría son cosas que también se pueden hacer a mano en muy poco tiempo
      Tampoco he observado un aumento de software realmente impresionante, lo que parece coincidir con el hecho de que hoy los LLM se usan para resolver problemas simples más que problemas importantes
    • Es muy probable que quienes sienten el mayor beneficio con los LLM sean personas que desde el inicio no sabían crear buen software o no tenían la capacidad para hacerlo bien
    • A mí también me pareció raro lo de los 7 meses. Incluso escribiéndolo en un lenguaje nuevo, no creo que tomara tanto tiempo
      Además, algo de lo que no se habla mucho es la calidad del código
      Un codebase hecho con vibe coding es un gran ejemplo de que los LLM no son tan buenos escribiendo código. Corrigen sus propios errores y enseguida los vuelven a introducir, y tampoco usan patrones con consistencia
      Últimamente Claude a veces toma decisiones de estilo de código “interesantes” que no encajan con el estilo actual del codebase
    • La familia GPT, por su propia naturaleza y propósito de generar texto —es decir, lenguaje y código—, parece estar internamente sesgada hacia hacerlo todo directamente
      Hay que frenar esa repetición con lenguaje tipo “desarrollador senior”
  • La parte de “antes de escribir código, diseño yo mismo interfaces concretas, tipos de mensajes y reglas de ownership” es precisamente la parte difícil de programar
    Si ya tienes la arquitectura, escribir el código es muy fácil. Si no escribes el código tú mismo, es más difícil darte cuenta de que diseñaste una API que permite null pero la base de datos no, o que aunque lo permita, igual dejaste pasar otros problemas pequeños
    No entiendo cómo incluso después de escribir este texto no se dio cuenta de que el problema era la IA. No solo porque le dejó la arquitectura a la IA, sino porque no miró con atención todo lo que la IA iba haciendo
    La IA es un generador de código embellecido, y hay que verificar todo lo que hace. La parte difícil de la ingeniería de software nunca fue escribir código, sino todo lo demás

    • Creo que hay dos tipos de desarrolladores: los que creen que la parte difícil es el código y los que no
      Los desarrolladores que creen que programar es la parte difícil aman de verdad el AI coding, porque algo que antes era difícil ahora se volvió fácil
      En cambio, para quienes creen que escribir código es fácil, programar es un problema de abstracción, mantenibilidad y escalabilidad. Lo difícil es sentar una base sensata para que el software pueda crecer; si encuentras la abstracción correcta, lo demás es relativamente sencillo
      Para este tipo de personas, el AI coding es una herramienta útil, pero no una herramienta mágica. El autor original notó los límites de la IA, así que pertenece al segundo grupo y vio la parte difícil que la IA no puede hacer
    • Ahora mismo hay un problema de definiciones confusas
      De un lado están quienes usan un autocompletado potente o un chatbot al lado, pero revisan claramente todo; del otro, casos como el de Steve Yegge promocionando un nuevo editor para coordinar decenas de agentes como si no fuera a leer la mayor parte del código: https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16d...
      El primer grupo sigue pensando a fondo el diseño, las interfaces y las estructuras de datos, y revisa con fuerza. El segundo grupo no, y por eso preocupa más
    • Creo que los agentes casi siempre fallan entre la planificación y la ejecución
      Sigo un enfoque plan → red/green/refactor, y el plan en sí suele verse bastante plausible y bien fundamentado porque absorbe toda la documentación y discusiones de foros
      El problema es que cuando empieza el trabajo, inevitablemente aparece algún punto donde la documentación y la implementación real no coinciden. Puede ser que esa combinación de herramientas nunca se haya usado así, que la documentación esté desactualizada o simplemente que haya un bug
      Aun así, si el objetivo del proyecto o de la funcionalidad está lo bastante claro y se puede ejecutar y probar localmente, el agente puede quedarse iterando en un callejón sin salida arquitectónico y aun así salir de ahí. Incluso mira dependencias y código de librerías y propone correcciones upstream, algo bastante parecido a lo que yo haría en una sesión profunda de debugging
      Por eso estoy bastante conforme con el modo de dar instrucciones y supervisar en vez de hacer yo mismo las tareas aburridas. Pero una buena parte de mi equipo no profundiza tanto en problemas de arquitectura y su respuesta por defecto es “escalarlo al arquitecto”, así que no creo que eso sea bueno a largo plazo
      La ventana en la que uno puede ejecutar y entender todo parece cerrarse rápido. Aun así, del mismo modo que usamos compiladores sin entender por completo cómo convierten a código máquina o cómo funcionan totalmente la predicción de saltos y la caché de CPUs modernas, quizá nos adaptemos creando nuevas herramientas y frameworks
    • Parece que mucha gente pasa por alto que hay que verificar todo lo que hace la IA
      Desde la perspectiva de alguien sin tanta experiencia programando, estoy aprendiendo más que nunca al verificar los resultados y ver qué está bien y qué está mal
      Por eso tampoco parece que vaya a mejorar radicalmente pronto. Cuando la gente me pregunta “¿cómo haces para que la salida de Claude sea tan buena?”, la respuesta siempre es “la miré con cuidado, encontré problemas y le pedí a Claude que los corrigiera”. De verdad eso es todo, pero apenas lo oyen ya se les apaga la mirada
      Es como Google: facilitó muchísimo encontrar información, pero no eliminó el factor humano necesario para distinguir la buena de la mala
    • Esta fue la única forma que encontré de usar agentes sin terminar odiándolos por completo o sin fracasar
      Primero pienso el problema y diseño la estructura y la API; solo entonces le encargo a la IA la implementación
  • El título dice “volví a escribir código a mano”, pero en realidad lo que hace es “hacer a mano el trabajo de diseño antes de que se escriba el código”
    Entonces parece que el código todavía lo genera Claude
    Más grave aún: cuesta entender que durante 7 meses pensara que su proyecto de vibe coding funcionaba bien sin ni siquiera mirar el código fuente generado, y que además hasta compró el dominio

    • En pocas palabras, es un título clickbait y el objetivo del texto parece ser atraer atención hacia su proyecto
    • Yo he comprado el dominio de un proyecto a los pocos minutos de que se me ocurra la idea
      Si es un side project y vas verificando gradualmente siguiendo los diffs, no es tan raro no mirar el código a fondo. Sin duda es otra forma de trabajar, pero tampoco es una locura total
  • Se siente como ver a desarrolladores hacer un speedrun de las lecciones de gestión de proyectos y gestión de producto
    Ahora están viendo que las especificaciones sí son útiles y que escribir mucho código incorrecto no hace que un proyecto vaya más rápido. Los desarrolladores se molestan porque reuniones y discusiones les interrumpen la escritura de código, pero muchas veces esos procesos existen precisamente para evitar que todos escriban más de algo equivocado
    También se dieron cuenta de que la gestión del trabajo es útil, y ahora que se habla cada vez más de diseñarlo todo por adelantado, van camino al waterfall
    Lo siguiente será ponerle nombre al prototipado, hablar de funcionalidades incrementales que gestionan requisitos viejos y nuevos al mismo tiempo, y al final dirán que hace falta más involucramiento del cliente
    Hace falta ver qué hacen realmente los project managers y product managers. Ellos conducen un producto que es código, pero no se espera que lean el código, y tienen que lograrlo solo con lenguaje natural

    • Tal cual. Parece que esta gente nunca fue manager
      ¿Creen que los humanos no escriben cosas rotas? ¿Que no pasa que un equipo se va por el camino equivocado y quema una semana o incluso meses? Ahora con vibe coding puedes vivir todo eso en 30 minutos. Como ex technical product manager, se siente exactamente igual
  • En realidad no parece que esté escribiendo código a mano, así que la diferencia entre el título y la conclusión confunde

    • Creo que la intención era poner un título sensacionalista para que HN mordiera y lo subiera a portada
    • El texto tampoco parece escrito a mano. Lo que llegó arriba de HN no fue el artículo, sino el título mismo