El arte de destruir software 🥋
El código que escribiste hoy probablemente esté mal.
Hay una verdad a gritos que la industria del software lleva décadas resistiendo con una admirable consistencia: el código que escribiste hoy probablemente esté mal.
No porque seas malo programando. Sino porque cuando empezaste, no podías predecir lo que ibas a descubrir por el camino. El problema con el software no es escribirlo, es que generalmente lo tienes que escribir antes de entender el problema que se está resolviendo de manera absoluta. Y cuando finalmente lo entiendes, ya tienes varias capas de abstracción y conexiones encima que hacen que cambiar algo duela.
La solución que propone la industria para esto es, habitualmente, más abstracción. Más capas. Más patrones de diseño con nombres en latín. Una arquitectura tan intrincada y compleja que hacer debugging es como hacer arqueología, ir desenterrando huesos y tratando de entender qué era lo que el programador original tenía en mente.
Sin embargo, hay una solución mucho más simple: ¿qué tal si simplemente aceptamos que el código que escribimos hoy va a estar mal, y diseñamos nuestro software para que sea fácil de destruir y volver a escribir todas las veces que sea necesario?
La charla que nadie quiere dar
Greg Young tiene una charla a la que llamó “The Art of Destroying Software”. Una de las más sinceras y valientes que he visto. En este artículo voy a resumir los puntos más importantes basado en mi propia experiencia e interpretación.
“The Art of Destroying Software”
Young arranca la charla preguntando cuántas personas han ido a una conferencia sobre borrar código. El silencio en la sala lo dice todo.
Hay miles de charlas sobre cómo escribirlo. Sobre cómo refactorizarlo. Sobre cómo testearlo. Sobre arquitecturas que garantizan que sobreviva más que tú. ¿Pero sobre borrarlo? Nada. Como si el código fuera una inversión sagrada que hay que proteger a toda costa, no una hipótesis sobre un problema que todavía no entendemos bien.
Young menciona el paper “Big Ball of Mud” (Gran Bola de Lodo) y dice algo que incomoda: esa gran bola de lodo no es un fracaso de diseño. Es el estado natural del software bajo presiones económicas reales. Si tu sistema no es una gran bola de lodo, quizás simplemente no tienes suficientes usuarios como para que importe.
La solución que propone el paper tampoco es la que esperarías: no es “mejora tu arquitectura”. Es “crea pequeñas bolas de lodo dentro de la grande”. Islas manejables. Zonas donde puedas entrar, quemar todo, y salir en un tiempo razonable.
Ese es el principio que se propone: optimiza para el delete.
La métrica que nadie mide
Young propone una regla que suena simple y que casi nadie aplica: ninguna parte de tu sistema debería tomarte más de una semana reescribir desde cero.
No es un objetivo de sprint sino un criterio de diseño.
Si un componente tarda más de una semana en reescribirse, ese componente es zona de riesgo. No porque el código sea malo, sino porque ya no lo controlas; él te controla a ti. Y cuando llegue un feature nuevo que no encaja con el modelo actual, vas a pasar dos semanas peleando con el modelo en lugar de dos días escribiendo uno nuevo.
Young cuenta que estaba trabajando en Event Store DB, descubrió que su modelo no servía para lo que necesitaba agregar, y reescribió todo el backend en dos días. “Dos días”. Su estimación es que si hubiera intentado adaptar el código existente, habrían sido dos semanas de sufrimiento para llegar al mismo lugar.
La pregunta correcta no es “¿cuánto costó escribir esto?” sino “¿cuánto costaría borrarlo y volver a empezar mañana?” Si la respuesta te da miedo, ya perdiste el control de tu sistema.
Micro no es un tamaño, es una garantía
Cuando la industria adoptó microservicios, tomó la palabra “micro” y, con la creatividad que la caracteriza, la interpretó como una restricción de responsabilidad de equipo. Cada equipo tiene su servicio, cada servicio tiene su repo, cada repo tiene su pipeline. El criterio de corte no fue el dominio sino el organigrama: si hay un equipo de “pagos”, hay un servicio de pagos. Si hay un equipo de “usuarios”, hay un servicio de usuarios. Conway’s Law disfrazada de arquitectura. Debatieron en conferencias, escribieron libros, dibujaron diagramas con flechas que conectan cajas con nombres de equipos. Misión cumplida.
Eso es perder completamente el foco.
“Micro” no describe el tamaño ni la complejidad. Describe la garantía: este servicio tiene una sola razón para existir (responsabilidad única), y por lo tanto una sola razón para cambiar. Si mañana cambia el negocio, solo una parte específica del sistema necesita moverse. El resto queda intacto. Y si la parte que se mueve resulta ser irreconocible con los nuevos requerimientos, la borras y la reescribes. En una semana.
Un servicio con dos responsabilidades no se puede borrar a medias. Cuando intentas reescribir “la parte de autenticación” descubres que está mezclada con “la parte de perfil de usuario” que comparte datos con “la parte de notificaciones”. Lo que parecía una cirugía menor termina siendo un trasplante de órganos con el paciente despierto.
La responsabilidad única no es un principio estético de código limpio. Es la condición necesaria para que el delete sea posible.
El refucktor
Una de las mejores partes de la charla es cuando Young destruye la idea de refactoring tal como la practica el 90% de la industria.
Su definición: refactoring es cuando cambias o el código o los tests. Uno de los dos. El que no tocas es tu medición; la evidencia de que no rompiste nada.
Lo que hace la mayoría: cambiar código y tests al mismo tiempo. Young tiene un nombre para eso: refucktor. No es un refactor, es reescribir con miedo a admitir que estás reescribiendo.
Y lo mejor: las herramientas de refactoring modernas activamente te empujan hacia el refucktor. Te ofrecen cambios automáticos que tocan código y tests en el mismo movimiento. Conveniente. Y completamente inútil como mecanismo de validación.
Si puedes borrar el componente y reescribirlo en una semana, el “refactor” que estabas planeando de tres meses se convierte en un problema diferente. Ya no necesitas refactorizar. Reescribes, con el conocimiento que no tenías cuando lo escribiste por primera vez.
La deuda técnica y la hipocresía colectiva
Young tiene también una postura poco popular sobre la deuda técnica: no es inherentemente mala.
Supongamos que tienes una hipoteca. ¿Es mala deuda? Te permitió comprar una casa que no podías pagar al momento. La deuda técnica hace lo mismo: te da velocidad de salida a cambio de trabajo futuro. El problema no es tenerla. El problema es no saber cuánto tienes, ni poder pagarla porque todo está tan enredado que nadie quiere tocar nada.
Si un componente puede reescribirse en una semana, la deuda técnica dentro de ese componente deja de ser terrorífica. ¿Codeaste rápido y quedó feo? Bien. Cuando acumule suficiente mugre, lo borras y empiezas de nuevo con todo lo que aprendiste. No hay sprint de “reducción de deuda técnica”. No hay reunión con el CTO para explicar por qué seis meses sin features nuevos es una buena idea.
Solo borras. Y reescribes mejor.
El conocimiento llega al final
Hay una pregunta que Young hace y que debería estar tatuada en la frente de cada persona que haya participado en una reunión de planificación de tres días al inicio de cualquier proyecto:
¿Cuándo sabes más sobre tu sistema? ¿Al principio, cuando lo estás planeando, o al final, después de haberlo construido?
Al final. Siempre al final. Después que te enfrentaste a los problemas reales, después que viste cómo se comporta el sistema bajo carga, después que viste qué partes son realmente críticas y cuáles no, después que recibiste esas decenas de cambios de requisitos que no podías predecir. Recién ahí sabes lo que estás haciendo.
Diseñas la arquitectura cuando menos sabes. Tomas las decisiones más importantes cuando tienes menos información. Y después pasas años pagando el costo de esas decisiones; no porque fueran estúpidas, sino porque eran inevitablemente prematuras.
La única respuesta razonable es diseñar sabiendo que vas a estar equivocado. No para evitar el error sino para que el error sea barato. Que cuando llegue el momento en que entiendes el problema de verdad, puedas tirar lo que construiste antes y hacerlo bien, sin que eso sea un proyecto de seis meses.
Los eventos te obligan a pensar
Event-driven architecture tiene una reputación de ser la solución al acoplamiento entre servicios. Es correcto, pero es el beneficio secundario. El primero es menos obvio y más importante: los eventos te fuerzan a tener responsabilidades claras.
Cuando diseñas una llamada directa entre servicios, puedes ser vago. “El servicio de pedidos le avisa al servicio de inventario que actualice el stock.” Nadie pregunta nada. El acoplamiento se esconde en la conveniencia.
Cuando diseñas un evento, no puedes ser vago. Tienes que decidir qué ocurrió desde la perspectiva del dominio que lo emite. Y esa decisión te obliga a entender exactamente de qué es responsable ese dominio, y dónde termina su responsabilidad.
order.confirmed no le dice a nadie qué hacer. Describe algo que ocurrió. El servicio de inventario decide si le importa. El servicio de notificaciones decide si le importa. El servicio de facturación decide si le importa. El dominio de pedidos no sabe que existen, y no le tiene que importar.
El resultado práctico: cada servicio tiene exactamente una razón para cambiar. Si cambia la lógica de inventario, solo cambia inventario. Si cambia la lógica de notificaciones, solo cambia notificaciones. El dominio de pedidos no se entera de nada de eso porque nunca supo que esas cosas existían.
Eso es single responsibility en serio. No como principio en un libro, sino como consecuencia inevitable del diseño. Y un servicio con una sola razón para cambiar es un servicio que puedes reescribir en una semana sin miedo a lo que rompe del otro lado, porque del otro lado solo hay consumidores de un hecho que ocurrió, no dependientes de cómo lo implementaste.
El problema no es Clean Architecture. Eres tú. Bueno, no, en realidad sí es Clean Architecture.
Hablemos de la promesa que más veces he visto incumplida en producción.
Clean Architecture te dice: separa tu lógica de negocio de los detalles de infraestructura. Pon interfaces en el medio. Así, cuando el día de mañana necesites cambiar la base de datos, solo tocas una capa. El resto queda intacto. Suena perfecto. Suena exactamente a optimizar para el cambio.
Lo que pasa en la práctica: implementas un microservicio con casos de uso, repositorios, providers, datasources, cada uno escondido detrás de su interfaz correspondiente. Cuatro capas de abstracción para llegar a leer una fila de una base de datos. El día que decides migrar de DynamoDB a MongoDB; por costos, por features, por lo que sea; la promesa era clara: tocas solo el datasource, implementas la interfaz nueva, listo.
Y no. Tuviste que tocar en todos lados. Los modelos no mapeaban igual. Los queries asumían la semántica de DynamoDB. Los casos de uso tenían detalles de infraestructura filtrados hacia arriba sin que nadie se diera cuenta. La abstracción que debía aislar los cambios los había distribuido por todo el código de manera invisible.
La conclusión fácil es “lo implementaron mal”. Probablemente verdad. Pero esa es exactamente la trampa: una arquitectura tan compleja que implementarla correctamente requiere un nivel de disciplina y comprensión que la mayoría de los equipos no tiene, no es una buena arquitectura. Es una arquitectura que cobra las contrapartes siempre y entrega los beneficios solo en condiciones ideales que no existen.
Una interfaz es una promesa de que va a existir más de una implementación real. Si hoy solo tienes una implementación, no tienes una interfaz lo que tienes es burocracia con sintaxis. Y es esa burocracia la que hace que cambiar algo simple requiera tocar diez archivos en cuatro capas distintas.
La pregunta que hay que hacerse antes de introducir cualquier abstracción es brutal en su simpleza: ¿tengo ya dos implementaciones reales de esto, o solo estoy anticipando una flexibilidad que quizás nunca necesite? Si la respuesta es no, simplemente no abstraigas nada; cuando llegue el momento de cambiar, borras y reescribes. Esa es la idea de optimizar para el delete.
El código simple que puedes reescribir en tres días gana siempre al código elegante que nadie entiende del todo y que tardas tres semanas en modificar.
Unix lo sabía. Nosotros lo olvidamos.
Young cierra con algo que me parece lo más importante de toda la charla: esto no es nuevo.
Microservicios, SOA, actores, objetos; todo es el mismo concepto reciclado con nombres distintos cada diez años. La filosofía Unix de los 70s ya lo decía: programas pequeños, que hacen una cosa, que se componen. ¿Tienes miedo de borrar y reescribir grep desde cero? No. Porque grep es pequeño, hace una sola cosa, y lo entiendes en una tarde.
El problema no es que la industria no sepa esto. El problema es que lo sabe, adopta la forma - microservicios, SOA, actors - y pierde completamente el foco. El objetivo no era tener servicios pequeños. El objetivo era poder borrarlos sin miedo. Que si un servicio no asimila un nuevo requerimiento, no tengas que pasar semanas peleando con lo que tienes sino que puedas reconstruirlo en pocos días con un diseño que sí soporte todo lo que ahora entiendes.
Microservicios sin deleteability no es una arquitectura. Es un monolito con más servidores.
El miedo como métrica
Young lanza una pregunta casi al pasar, sin dramatismo, que es probablemente la más importante de toda la charla:
“¿Puedes imaginar trabajar como developer y no tener miedo?”
El miedo al código propio es tan común que dejó de verse como un problema. Es parte del trabajo. “Hay que tener cuidado con esa parte.” “Eso no lo toques que se rompe todo.” “Mejor no cambies nada cerca de ahí.” Frases que se normalizaron hasta volverse invisibles.
Pero son el síntoma más honesto de un sistema que ya no puedes destruir. No el código legacy, no la deuda técnica, no los tests que fallan en CI pero “en prod funciona bien”. El miedo. Ese es el indicador real.
Un sistema optimizado para el delete no te da superpoderes técnicos. No te hace más inteligente ni más productivo en el sentido abstracto. Simplemente te quita algo que no deberías tener en primer lugar: la ansiedad de tocar tu propio trabajo.
Cuando puedes borrar cualquier parte y reescribirla en una semana, el miedo desaparece. No porque el código sea perfecto, sino porque su imperfección dejó de ser permanente.
Optimiza para el delete. No porque vayas a borrar todo mañana. Sino porque el sistema que puedes destruir sin miedo es el único sistema que puedes cambiar sin miedo. Y un sistema que puedes cambiar sin miedo es el único que envejece bien.
Todo lo demás es arqueología con Kubernetes encima.