5 puntos por GN⁺ 2025-06-18 | 1 comentarios | Compartir por WhatsApp
  • La complejidad es el elemento más peligroso en el desarrollo
  • La verdadera eficiencia surge de un enfoque pragmático que evita la complejidad, como una "solución 80/20"
  • Es importante mantener una postura equilibrada y flexible respecto a las pruebas y el refactoring
  • Se enfatiza el uso de herramientas y la adopción del hábito de escribir código fácil de leer y mantener
  • Se recomienda desconfiar de la abstracción excesiva y de las modas, y perseguir la simplicidad

Introducción

  • Este texto es una recopilación de ideas de un desarrollador con cerebro de Grug, que resume lo aprendido con la experiencia tras muchos años desarrollando software
  • El desarrollador con cerebro de Grug no se considera muy inteligente, pero ha aprendido mucho programando durante mucho tiempo
  • Comparte sus descubrimientos de forma simple y graciosa, con la esperanza de que otros aprendan de los errores
  • La complejidad es, sin duda, el mayor enemigo en la vida de desarrollo
  • La complejidad se infiltra sigilosamente en la base de código y hace que incluso el código que al principio era fácil de entender termine siendo casi imposible de modificar

Cómo lidiar con el demonio de la complejidad

  • La complejidad se filtra en silencio como un espíritu invisible, y muchas veces los project managers y los desarrolladores que no son Grug no la perciben bien
  • La mejor forma de impedir la complejidad es decir "no"
    • "No voy a crear esta funcionalidad"
    • "No voy a introducir esta abstracción"
    • Claro, para la carrera profesional puede convenir más gritar "sí", pero el desarrollador con cerebro de Grug valora elegir con honestidad consigo mismo
  • Dependiendo de la situación, también hace falta transigir (“ok”), y en esos casos se prefiere resolver el problema de forma simple con una solución 80/20 (aplicando el principio de Pareto)
  • No contarle todo al project manager y en realidad sacarlo adelante con un enfoque 80/20 también puede ser una estrategia inteligente

Estructura del código y abstracción

  • La unidad adecuada del código (cut point) se revela de manera natural con el tiempo, por eso conviene evitar la abstracción temprana
  • Un buen cut point idealmente tiene una interfaz estrecha con el resto del sistema
  • Los intentos de abstracción prematura suelen fracasar, y los desarrolladores con experiencia tratan de estructurar el código poco a poco cuando su forma ya está algo asentada
  • Los desarrolladores con menos experiencia o con “big brain” tienden a intentar demasiada abstracción al inicio del proyecto, dejando una carga de mantenimiento

Estrategia de pruebas

  • Es importante tener equilibrio, no obsesión, con los tests
  • Se prefiere escribir tests después de hacer prototipos y cuando el código ya está relativamente asentado
  • Los unit tests se usan al principio, pero en la práctica las pruebas de nivel intermedio (integración) son las que más impacto tienen
  • Los tests end-to-end también hacen falta, pero si hay demasiados se vuelven imposibles de mantener, así que conviene dejar solo unas pocas rutas realmente necesarias
  • Cuando llega un bug report, primero se agrega una prueba de reproducción y luego se corrige el bug

Proceso, agile y refactoring

  • Agile no le parece mal al desarrollador Grug; no es lo peor, pero depositar demasiadas expectativas en los “chamanes agile” es peligroso
  • El prototipado, las herramientas y buenos colegas son factores de éxito mucho más importantes
  • El refactoring también es un buen hábito, pero los refactorings grandes y forzados son riesgosos
  • Introducir abstracciones complejas a la fuerza puede terminar causando el fracaso del proyecto

Mantenimiento, perfeccionismo y humildad

  • Es riesgoso desarmar un sistema existente sin motivo, y no es una buena práctica eliminar a ciegas una “estructura cuyo propósito no entiendes”
  • El idealismo de soñar con código perfecto en la práctica suele causar problemas
  • Con más experiencia, uno aprende en carne propia que hay que “respetar el código que funciona”

Herramientas y productividad

  • Las buenas herramientas de desarrollo (autocompletado del IDE, debugger, etc.) elevan mucho la productividad, y es importante conocerlas a fondo
  • Se enfatiza que el valor real de los sistemas de tipos está en el “autocompletado” y en prevenir errores, y que la abstracción excesiva y los genéricos pueden ser más bien peligrosos

Estilo de código y repetición

  • Se recomienda un estilo como dividir las condiciones en varias líneas, para que el código sea más fácil de leer y depurar
  • Se respeta el principio DRY (Don’t Repeat Yourself), pero se enfatiza que el equilibrio importa más que eliminar código repetido a la fuerza
  • Muchas veces una repetición simple es mejor que una implementación DRY complicada

Principios de diseño de software

  • Se prefiere la localidad del comportamiento por encima del principio de SoC (Separación de responsabilidades), sosteniendo que “si el código que realiza cierta acción está en ese objeto, mantenerlo es más fácil”
  • Se advierte que callbacks/closures, sistemas de tipos, genéricos y abstracciones deben usarse con moderación y de forma apropiada
  • El abuso de closures puede crear “callback hell” en JavaScript

Logging y operación

  • El logging es muy importante: conviene dejar registros en cada ramificación principal y, en entornos cloud, permitir el rastreo con cosas como request IDs
  • Si se pueden usar niveles de log dinámicos y logs por usuario, eso ayuda mucho a rastrear problemas en producción

Concurrencia y optimización

  • En concurrencia, solo se confía en modelos lo más simples posible (requests web sin estado, worker queues separadas, etc.)
  • Se recomienda optimizar solo después de obtener datos reales de profiling de rendimiento
  • Hay que prestar atención a costos ocultos como network I/O; fijarse solo en la complejidad de CPU es peligroso

Diseño de API

  • Una buena API debe ser fácil de usar, y los diseños o abstracciones demasiado complejos dañan la experiencia del desarrollador
  • Se recomienda una estructura con una “API simple para casos de uso comunes” y una “API en capas que también permita implementar casos complejos”

Desarrollo de parsers

  • Los parsers de descenso recursivo están subvalorados en la academia, pero en código de producción real suelen ser el método más adecuado y más fácil de entender
  • En la mayoría de las experiencias desarrollando parsers, los parsers generados por herramientas terminan siendo tan complejos que más bien empeoran la resolución de problemas
  • Como libro recomendado, destaca "Crafting Interpreters" como el mejor y lleno de consejos prácticos

Frontend y modas

  • El frontend moderno (React, SPA, GraphQL, etc.) más bien invoca todavía más al demonio de la complejidad, y muchas veces es innecesario
  • El propio Grug prefiere reducir complejidad con herramientas simples como htmx e hyperscript
  • En frontend se prueban cosas nuevas todo el tiempo, pero conviene recordar que muchas son solo repeticiones de ideas ya conocidas

Factores psicológicos e impostor syndrome

  • A muchos desarrolladores les pasa que sienten “no sé lo que estoy haciendo”, y hace falta liberarse del fenómeno FOLD (Fear Of Looking Dumb)
  • Si un desarrollador senior dice públicamente “esto también me cuesta, es demasiado complejo”, los desarrolladores junior pueden quitarse parte de la presión
  • El impostor syndrome es una sensación común, y se anima a seguir aprendiendo y creciendo

Conclusión

  • En programación, siempre hay que desconfiar de la complejidad, y mantener la simplicidad es la clave para un desarrollo exitoso
  • La experiencia, el uso efectivo de herramientas, la humildad y el respeto por el código que realmente funciona conducen a largo plazo a un desarrollo más eficiente y valioso
  • “La complejidad es muy, muy mala”: hay que recordar siempre esa frase

1 comentarios

 
GN⁺ 2025-06-18
Opiniones de Hacker News
  • Yo valoro tanto a un buen depurador que sería imposible medir su valor con piedras; en realidad me parece aún más impresionante. Tanto en startups pequeñas como en equipos famosos de Big Tech, muchas veces yo era la única persona del equipo que usaba un depurador. En la práctica, vi que mucha gente todavía depura con sentencias print. Incluso cuando intento mostrarles mi flujo de trabajo a mis colegas, no reaccionan. Estoy de acuerdo en que el mejor punto de partida para entender un sistema es justamente el depurador. Poner una pausa en una línea interesante durante una prueba y ver la pila es muchísimo más fácil que seguir el código solo en la cabeza. Aprender a usar un depurador de verdad te da una especie de superpoder pequeño pero real. Si puedes, de verdad recomiendo probarlo al menos una vez
    • De verdad quiero usar un depurador real, pero desde la perspectiva de alguien que solo ha trabajado en grandes empresas, en la práctica era imposible. En una arquitectura de malla de microservicios no puedes ejecutar nada localmente, y en la mayoría de los casos incluso en entornos de prueba está configurado para que no puedas conectar un depurador paso a paso. Así que la depuración con print es la única opción posible. Incluso si hasta el sistema de logs tiene problemas, o si el programa simplemente se cae antes de imprimir logs, ni siquiera print sirve
    • Hubo una buena discusión sobre este tema hace años. Hay una cita famosa de Brian Kernighan y Rob Pike, y ninguno de los dos es precisamente un desarrollador joven. "No usamos depuradores para mucho más que ver un stack trace o revisar algunos valores de variables. Con estructuras de datos complejas y flujos de control complejos, es muy fácil quedar atrapado en los detalles. Es más productivo pensar más directamente sobre el programa, e ir agregando salidas con print y código de autocheck en el camino. Agregar un print es mucho más rápido que entrar paso a paso con un depurador. Además, el código con print se queda en el programa, mientras que la sesión de depuración desaparece." Yo también estoy de acuerdo con esto. Durante la mayor parte del desarrollo, el ciclo print-hipótesis-ejecución da una resolución de problemas mucho más rápida. No es que "ejecute" el código en la cabeza, sino que ya tengo un modelo mental de cómo fluye, así que cuando un print muestra algo incorrecto, por lo general capto muy rápido qué está pasando en realidad. Enlace relacionado: The unreasonable effectiveness of print debugging
    • La razón por la que la depuración con printf siempre fue tan común en el mundo Linux es que el entorno no permitía confiar en depuradores con GUI. Las GUI de Linux suelen ser inestables y no se puede confiar demasiado en ellas. En mi caso, empecé a usar depuradores en serio cuando (1) en Windows la GUI funcionaba bien pero la CLI a veces se rompía, y (2) después de varias veces en que código de depuración con print terminó colándose por error en una versión y causando problemas. Luego tuve varias aventuras con depuradores de CLI, y sentí que el proceso de usar Junit + depurador (en IDEs como Eclipse), probando código experimental directamente y dejándolo como prueba, era tan cómodo como un Python REPL. Claro, sí hace falta una inversión inicial para dejar bien configurado el depurador según el entorno
    • En mi propio código es fácil usar el depurador y me encanta. Pero en cuanto el depurador se mete más allá de lo que yo escribí y entra profundo en bibliotecas o frameworks, yo también me pierdo de inmediato y me deja de gustar. Esos frameworks o bibliotecas fueron construidos con decenas de miles de horas de trabajo, así que para mí enseguida se salen de mi rango de comprensión
  • Profesor Carson, si por casualidad ve este texto, quiero darle las gracias de corazón. En la universidad no entendía por qué estábamos aprendiendo HTMX ni por qué usted le ponía tanta pasión, pero años después por fin lo comprendí bien. HTML over the wire realmente lo es todo. Trabajando como Staff Ruby on Rails Engineer, vi su trabajo muchas veces también en Hotwire, y de vez en cuando me sorprende verlo activo en GitHub o Hacker News. Siempre ha sido una especie de luz para la comunidad de programación. Le tengo un respeto y una gratitud profundos
    • No soy el único al que esto le llega, de verdad conmueve
    • ¿HTMX no era solo un meme? Con la ley de Poe ya ni sé si esto va en serio
  • Este texto está lleno de frases buenísimas, pero a mí la que más me gustó fue la parte sobre microservicios: "grug no entiende por qué big brain, que ya tiene dificultades para dividir bien un sistema, además decide agregar llamadas de red"
    • Algunas personas solo saben dividir un sistema en partes convirtiéndolas en APIs. Si no está expuesto como API, lo consideran simplemente código opaco que no se puede entender ni reutilizar
    • También es una lástima que, por varias razones, a veces los microservicios sí sean prácticos y por eso se usen
    • Sigo viendo que incluso para una web app insignificante con apenas cinco formularios, equipos pequeños de dos desarrolladores la complican armando una estructura de "microservicios" (base de datos compartida, gestión de APIs, trabajos por lotes mediante colas, notificaciones por correo, además de una plataforma propia de observabilidad, etc.). Y al final hasta los formularios normales los convierten en SPA porque supuestamente "es más fácil". Ya entendí que la "arquitectura" y los "patrones" son una forma de inventar trabajo para desarrolladores inútiles. Si no existieran esas cosas, serían personas en la calle con un cartel diciendo "usaré JavaScript por un trozo de sándwich"
    • Mi teoría conspirativa es que los vendors de nube impulsaron el patrón de microservicios y por eso terminamos así. - Hacen que no puedas ejecutar nada sin orquestadores como K8S, y así es más fácil vender nube administrada - Más tráfico de red y más uso de CPU generan más cobros - Como compartir estado a gran escala se vuelve difícil, terminas necesitando bases de datos administradas y colas de eventos - Como correr todo localmente se vuelve complicado, hasta el entorno de desarrollo se convierte en costo de nube - Te atan a formas específicas de la nube y luego es difícil salir. Antes la nube se anunciaba como una forma de ahorrar costos de TI, lo cual da risa. Ya desde los 2000 se sabía que eso era una ilusión, y al final todo solo termina siendo más caro
  • La frase "complejidad vs tiranosaurio cara a cara, grug elige al tiranosaurio antes que a la complejidad: al menos al tiranosaurio sí se le puede ver" me impresionó tanto que la recuerdo como una vez por semana
    • Cita: "Mientras caía, Leyster no soltó la pala. En medio del pánico, había olvidado ese detalle. Entonces, desesperadamente, blandió la pala contra la pierna del tiranosaurio joven...". Es una escena que describe de manera vívida una lucha extrema por sobrevivir contra un tiranosaurio. Al final, su compañera Tamara supera la crisis al clavar valientemente una lanza justo en medio de la cara del tiranosaurio. Son muy impactantes tanto la batalla y la tensión como el momento de silencio
    • Claramente grug nunca ha peleado contra un tiranosaurio "invisible". Yo ahora mismo sigo en un duelo uno a uno con un tiranosaurio invisible, y de verdad la estoy pasando mal
  • Lo admirable de este artículo es que el autor sí podría hacer cosas "más complejas", pero por experiencia decide no irse por ese camino. Claro que hay momentos y lugares donde la abstracción o la complejidad hacen falta, pero la filosofía grug dice que esas cosas no tienen valor intrínseco por sí mismas. Esa parte me parece muy acertada. También siento que la IA funciona mejor con código consistente y basado en datos
    • Cuando uso complejidad y abstracción, es porque gracias a eso el código se vuelve más fácil de entender que antes. Hay que recordar siempre la condición de "cuando no hace falta tomar un curso extra solo para entenderlo". (Depende del caso)
    • "Todo debe hacerse tan simple como sea posible, pero no más simple"
  • Cuesta creer que este texto sea de 2022. Se siente como si ya lo hubiera leído hace 10 años y lo conociera como un "clásico"
  • Este ensayo es mi texto favorito sobre crear software. El estilo también tiene mucho encanto (aunque a algunos podría generarles rechazo), pero la esencia siempre sigue vigente
  • El fragmento "triste pero cierto: aprender a decir 'sí', y si falla aprender a culpar a otro grug, mejor estrategia de carrera" es muy real. Al principio yo también creía por error que en la empresa simplemente había un problema de comunicación entre equipos técnicos, pero con el tiempo aprendí (como grug) que de verdad era así
  • De todas las explicaciones del visitor pattern que he visto, la de este artículo es la mejor
    • Como no trabajo en una base de código OO típica, no entendía bien qué era el visitor pattern, así que quiero recomendar el libro sobre cómo hacer un intérprete/VM llamado "Crafting Interpreters". En ese libro se ve cómo se usa realmente el visitor pattern. Lo leí tratando de entender por qué existía tanta complejidad, pero al final lo reemplacé con tagged unions. Tal vez yo simplemente no soy bueno para OO, pero justo ese también es el punto del artículo grug: cuando no hace falta imponerse complejidad e indirección, hay métodos más intuitivos
    • Yo soy sensible con los nombres, y me molesta que visitor pattern sea un nombre tan vago. De hecho, nunca he creado algo llamado literalmente Visitor. Por ejemplo, en una práctica de árbol sintáctico (AST), nombres concretos como AstWalker, AstItem::dispatch(AstWalker) o AstWalker::process(AstItem) comunican mucho mejor la intención que Visitor. Eso de que "visita" algo es demasiado abstracto y casi no significa nada. Claro, depende del caso, y basta con dejar en un comentario que es un "visitor pattern" para que la gente lo reconozca. Hace tiempo, cuando tuve que comparar/importar datos haciendo coincidir dos árboles de objetos, usé el nombre AbstractImporter. Era un nombre más específico, y el proceso y el rol quedaban mucho más claros. No era un visitor pattern típico
    • De hecho, al buscarlo, vi una opinión que decía que era "Bad". Jaja
  • Comparto textos relacionados. ¿Alguien tiene otras opiniones o artículos adicionales?<br/><i>The Grug Brained Developer (2022)</i> - https://news.ycombinator.com/item?id=38076886 - octubre de 2023 (192 comentarios)<br/><i>The Grug Brained Developer</i> - https://news.ycombinator.com/item?id=31840331 - junio de 2022 (374 comentarios)