2 puntos por GN⁺ 2024-08-06 | 1 comentarios | Compartir por WhatsApp
  • La experiencia de reescribir durante un mes el núcleo de un programa que había usado y corregido personalmente durante dos años sacudió mis creencias previas sobre pruebas y control de versiones
  • En 2015, veía las pruebas y versiones como la clave del software duradero, más que las malas abstracciones, pero al pasar por Mu y Freewheeling Apps mi forma real de trabajar fue cambiando cada vez más
  • Considero que los programas duraderos no deben hacerse tanto para muchas personas, sino dentro de personas, contextos y funciones bien conocidos, aceptando límites realistas como el número de Dunbar
  • Tipos, abstracciones, pruebas, versiones, máquinas de estado, inmutabilidad y análisis formal son útiles en territorios desconocidos, pero en exceso se convierten en deuda técnica que oculta complejidad innecesaria
  • Cuando la comprensión del contexto se estabiliza, vale la pena desechar partes grandes y rehacerlas, y hay que cargar en la mente todos los escenarios necesarios a la vez para construir el todo de una sola vez

Cambio de perspectiva sobre las pruebas y el control de versiones

  • He seguido lidiando con el problema de elegir y crear personalmente programas de los que pueda depender por mucho tiempo, pero ni yo mismo siento que sea especialmente bueno en ello
  • Durante el último mes reescribí el núcleo de un programa que había usado y modificado gradualmente durante dos años
    • Después siguieron varios días para ordenar qué aprendí y hacia dónde ir después
    • A partir de este trabajo empecé a ver un cambio vital más amplio
  • En 2015 desconfiaba de las abstracciones y daba mucha importancia a las pruebas y al control de versiones
    • Veía mucho código con malas abstracciones, y consideraba que las pruebas y las versiones eran avances clave de la década de 2000
    • Buscaba las causas del problema en malos incentivos, abstracción excesiva, y pruebas y versiones insuficientes
    • Mu1 fue un intento de diseñar una plataforma que tomaba las pruebas y las capas como restricciones base
  • En 2017 empecé a retrabajar Mu1 hasta convertirlo en el Mu actual
    • Al principio usé todas mis ideas nuevas sobre pruebas y capas
    • Con el tiempo fui usando menos esas ideas
    • Actualmente Mu tiene muchas pruebas, pero la mayoría son pruebas comunes, y no logré trasladar la infraestructura de capas
  • En 2022 empecé a crear Freewheeling Apps
    • Al principio no tenía pruebas, y más tarde escribí pruebas exhaustivas para una pieza central: el editor de texto
    • Fue difícil encontrar cómo probar el resto, pero aun sin pruebas pude avanzar lo suficiente
  • En 2024 eliminé todas las pruebas
    • Empecé a rehacer en grande el editor de texto, y ese enfoque podría haberme hecho preocupar por conflictos de fusión con otras Freewheeling Apps
    • Como resultado, también dejé de pensar en el control de versiones
    • Tras abandonar las pruebas y las versiones y obtener un programa mejor, se volvió difícil seguir ignorando la disonancia cognitiva con mis creencias anteriores

Mi síntesis actual para programas duraderos

  • Creo que crear algo duradero para mucha gente es demasiado difícil, así que es mejor no intentar eso desde el principio
    • Hay que dejarse gobernar por lo que uno conoce bien, las personas que conoce bien y el número de Dunbar
  • Considero que la mayor parte del software del mundo está infectada por incentivos para servir a mucha gente en el corto plazo
    • En la medida de lo posible, me enfoco en software cuyos sitios web no tengan muchos logos
    • Prefiero software fácil de crear, con pocas dependencias y sin actualizaciones automáticas
    • Si se filtra con estas restricciones, la cantidad de software duradero que la humanidad ha creado hasta ahora es muy pequeña
  • Incluso cambios pequeños de contexto, como personas, lugares o funciones que se quiere soportar, pueden cambiar mucho qué tan bien se ajusta un programa a ese contexto
    • En un entorno dominado por el cortoplacismo, es difícil prepararse para este hecho
  • Como el volumen de trabajo pasado es pequeño y el alcance aplicable de cada programa también es bajo, cualquier programa que decida crear probablemente entrará de alguna manera en territorio desconocido
    • Incluso al intentar agregar “drawing lines” especiales a un editor de texto surgen varias preguntas
      • ¿Puede el cursor estar sobre un dibujo?
      • ¿Puede dibujarse en una línea cuando el cursor está en otra?
      • Si un dibujo es más alto que una línea de texto, ¿puede verse solo parcialmente en la parte superior de la pantalla?
      • ¿Se puede dibujar sobre un dibujo parcialmente visible?
    • Durante mucho tiempo las respuestas a estas preguntas no fueron óptimas, así que se apilaron parches temporales sobre más parches temporales

Las herramientas son necesarias, pero en exceso se vuelven deuda técnica

  • Tipos, abstracciones, pruebas, versiones, máquinas de estado, inmutabilidad y análisis formal son herramientas que pueden usarse en terreno desconocido
    • Úsalas en la medida necesaria y según tus gustos
  • Las personas tienden a abusar de las herramientas que les atraen
    • Creo que la cantidad ideal de uso de estas herramientas es muy pequeña
    • Debe ser mucho menor que la intuición aprendida en entornos dominados por el cortoplacismo
  • El uso excesivo de herramientas se convierte en deuda técnica
    • Hace más difícil notar que un programa es innecesariamente complejo
    • Hace que el programa dure menos de lo que podría durar
    • Hace más difícil modificar el programa cuando cambia el contexto

Reescrituras y “hacer todo de una sola vez”

  • Cuando la comprensión del contexto se estabiliza, vale la pena desechar partes grandes del programa y empezar de nuevo desde cero
  • Antes de reescribir, hay que llevar a la mente de una sola vez todo lo que se quiere del programa y todos los escenarios que debe manejar
    • Este proceso es difícil, pero el objetivo es llegar a un estado en el que se pueda construir todo de una sola vez
  • El método final es hacer todo de una sola vez
  • En esta experiencia, las pruebas y las versiones más bien obstaculizaron llegar al final de esta evolución
    • Las pruebas hacen olvidar los problemas por los que uno debe preocuparse
    • El control de versiones mantiene a uno aferrado al pasado
    • Ambos fueron contraproducentes, y dejarlos requería un gran cambio de rumbo
  • Considero que todo el software que he creado hasta ahora y Freewheeling Apps están en la etapa 6 de esta trayectoria

Límites de la complejidad y diseño orientado a datos

  • Si un programa se vuelve demasiado complejo, puede ser imposible cargarlo entero en la mente en la etapa 8
    • Creo que esto aplica a la mayor parte del software actual, en especial al software escrito por más de un par de personas
    • Incluso un pequeño editor de texto era abrumador, así que pasé mucho tiempo del mes preparándome para enfrentar ese miedo
  • No todo software necesariamente tiene que llegar hasta la etapa 9
    • Muchas Freewheeling Apps son suficientemente simples y evolucionan con lentitud
    • Creo que, con que las use un pequeño número de personas, pueden estabilizarse sin bugs sin importar las decisiones iniciales de diseño
    • En especial ahora que sé cómo simplificar una pieza compleja del núcleo
  • Aun así, es bueno saber cómo mejorar si surge el valor para hacerlo
  • Como método que parece claramente útil para llegar a la etapa 9, señalo el diseño orientado a datos
    • No es una herramienta que se pueda aplicar a ciegas, sino una forma de pensar que ve el panorama general de cómo un programa accede a los datos
    • Hay que evitar que herramientas como ECS oculten la actividad intelectual esencial
  • Esta división por etapas puede no ser del todo correcta
    • Puede que esté subestimando herramientas con las que tengo poca experiencia
    • Qué hay más allá de estas etapas sigue siendo una pregunta abierta
  • En el artículo sobre mi forma de programar que escribí en 2019 se pueden ver rastros de cómo ha cambiado mi pensamiento

1 comentarios

 
GN⁺ 2024-08-06
Opiniones de Hacker News
  • Si no hay pruebas, no se ven las pruebas fallidas, así que solo parece que el problema desapareció.
    Nunca me pasó que probara algo y no encontrara bugs, y la mayoría de las cosas que probé eran cosas que ya creía listas para salir.
    Si borras las pruebas, al final es muy probable que el único al que estás engañando seas tú mismo. Al leer el artículo, parece más bien que está cansado de la gestión de variantes/configuración que de las pruebas en sí, y eso es totalmente comprensible. Dicho eso, hace falta tener usuarios para ganar dinero, y si fuera un problema fácil, el mercado ya estaría saturado de soluciones universales.

    • Creo que esto depende del dominio. En algunas partes del codebase en el que estoy trabajando ahora, las pruebas ayudan mucho con la refactorización, pero en otras hay mucho comportamiento de UI y las pruebas manuales son mucho más rápidas.
      Si la UI o el workflow cambian demasiado rápido, uno deja de escribir pruebas porque sabe que en la siguiente iteración ya no servirán; y si cambian demasiado lento, esa parte no se vuelve a tocar, así que también hay menos riesgo de introducir bugs nuevos al refactorizar. Las pruebas o los tipos no son el santo grial universal, sino herramientas adecuadas para cada trabajo. Nunca he visto un codebase donde, aun con buena cobertura de pruebas, no se descubran bugs mediante pruebas manuales o uso real. Exagerando un poco: si eres lo bastante bueno como para escribir pruebas perfectas, entonces simplemente escribe código perfecto. Si no puedes escribir pruebas perfectas, ¿cómo sabes que esas pruebas son completas, no tienen bugs y de verdad son útiles?
    • La frase “las pruebas pueden mostrar la presencia de bugs, pero no pueden mostrar su ausencia” encaja mejor con mi experiencia.
      Cada pocos meses encontraba un bug nuevo y agregaba una prueba con disciplina, pero unos meses después alguien que lo usaba por primera vez durante 10 minutos volvía a encontrar otro bug. Seguramente en la nueva versión también habrá bugs por descubrir, pero creo que, gracias a la estructura de datos elegida, muchas de las pruebas antiguas dejaron de ser necesarias por diseño. Al menos para un uso ligero, espero que con solo atrapar unos cuantos bugs más quede bastante estable. Las pruebas son muy valiosas cuando un equipo grande modifica continuamente un codebase, pero aquí se está intentando construir algo con un conjunto fijo de funcionalidades.
    • Vi el video Go Testing By Example de Russ Cox que recomendaron en un hilo reciente: https://www.youtube.com/watch?v=X4rxi9jStLo
      Tiene muchísimos consejos útiles, pero algo que me interesa destacar en particular es que se puede probar contra una implementación más simple, por ejemplo una implementación de fuerza bruta. Ahí hay una sabiduría más profunda. La utilidad de una prueba depende de qué tan más simple sea la implementación de la prueba frente a la implementación que se está probando. Dicho con más fuerza: una prueba solo es útil cuando es más simple que lo que prueba. Por muchas pruebas que escribas, al final igual tienes que razonar sobre el código, y que algo sea una “prueba” no lo vuelve útil por sí solo. Por eso creo que muchos programadores desconfían de dividir funciones en fragmentos que no son interfaces útiles solo para que sean fáciles de probar, de probar helpers simples o consultas pequeñas solo por cobertura, o de introducir inversión de dependencias y mocking únicamente para las pruebas. Por supuesto, cada caso puede tener sus razones, pero lo importante es no perder de vista lo esencial.
    • En mi caso, cada vez que escribí pruebas unitarias, aparecieron bugs.
      Normalmente no sigo mucho el enfoque de desarrollo guiado por pruebas de escribir primero una prueba que falla, aunque a veces sí. Por eso estas pruebas suelen apuntar a código que yo creía que ya funcionaba. Dicho eso, normalmente prefiero un test harness a las pruebas unitarias[0]. Igual encuentra bugs, pero el flujo es menos lineal. Hace que pruebe más durante el desarrollo y pueda corregir los bugs en el momento.
      [0] https://littlegreenviper.com/testing-harness-vs-unit/
    • Centrarse en pruebas unitarias/de integración automatizadas es una tendencia relativamente moderna, quizá desde fines de los 90. Antes de eso también se lanzaba software bastante grande y muy estable.
      Por ejemplo, el kernel de Linux antes no tenía muchas pruebas, y hoy parece tener más. Unix tampoco habrá tenido muchas “pruebas”. Los compiladores sí solían tener pruebas, pero los sistemas operativos menos; y juegos como Doom probablemente tampoco tenían muchas. Al final hay que encontrar un punto de equilibrio. Sabemos que las pruebas automatizadas —unitarias, de integración y end-to-end— ayudan a crear software de calidad. Al mismo tiempo, no siempre es fácil escribir buenas pruebas; las malas pruebas dificultan la refactorización, y las pruebas inestables consumen muchísimo tiempo en proyectos grandes. Aun así, especialmente si desarrollas solo, es interesante probar varios enfoques y encontrar el que te funcione.
  • La parte de “renunciar a las pruebas y a las versiones hizo que el programa fuera mucho mejor” es difícil de entender. No sé quién en 2024 querría programar voluntariamente sin gestión de código fuente.
    Incluso en un proyecto de una sola persona, la capacidad de trabajar desde varios dispositivos, ver el historial, revertir cambios y usar ramas aporta muchísimo valor casi sin costo. Tal vez esté malinterpretando lo que el autor quiso decir con “versiones”.

    • Está intentando crear algo pequeño y rápido cuyo conjunto de funcionalidades queda fijo. Decidió construir sobre una base que no cambia con frecuencia, y hay más contexto en https://akkartik.name/freewheeling.
      Es cierto decir que este enfoque no encaja con la mayoría de los programas que la gente crea hoy, es decir, con equipos grandes y requisitos que cambian constantemente. Aun así, sigue usando control de código fuente. Como dice el texto original, solo dejó de preocuparse por causar conflictos de merge con otros forks. Ahora hay más de 24 forks, y hay más detalles en el enlace de arriba. Usa control de versiones para usos básicos como backups, “¿qué acabo de cambiar?” y poner el software en un dispositivo nuevo. Pero, al menos para este programa, dejó de pensar en el control de versiones como un medio para entender y rastrear qué cambió. Hay más detalles en https://akkartik.name/post/wart-layers. Por ejemplo, empezó a preocuparse menos por la higiene de los mensajes de commit. El control de versiones existe, pero en este contexto acotado de intentar crear un resultado durable, con un conjunto de funcionalidades fijo y pensado para durar décadas, bajó de prioridad como “buena práctica de programación”.
    • El autor no parece estar en una situación en la que tenga que dar soporte a usuarios profesionales o de pago, y parece querer más libertad para experimentar que garantizar una versión estable conocida.
      Tampoco parece estar lidiando con un sistema grande ni con trabajo importante en equipo. Bajo estas condiciones, las herramientas pueden no aportar mucho valor. Un flautista en una gran orquesta que interpreta una sinfonía compleja necesita partituras y un director, pero si toca solo con una drum machine o hace free jazz, las partituras pueden servir de poco e incluso estorbar.
    • El autor parece estar sufriendo fatiga mental o burnout con respecto a la programación. Si el control de versiones le molesta tanto, me parece una señal bastante clara de que debería descansar.
    • Los programadores están constantemente abrumados por elecciones y opciones. Las herramientas y el espíritu de la época que señala cuáles son las “mejores herramientas” suelen moverse en la dirección de facilitar alguna tarea.
      Pero si siempre hay 1000 opciones fáciles, elegir la correcta genera una gran carga cognitiva. Esa es también una de las razones por las que la industria sacraliza todo tipo de mejores prácticas y presiona socialmente a quienes no las siguen. Una mala arquitectura y un código espagueti terrible hacen que trabajar sea muy difícil, pero cuestionar cosas que parecen obviamente correctas y explorar un entorno de desarrollo estricto que reduzca opciones y herramientas puede permitir enfocarse más en el problema final. El control de versiones también incentiva a dividir el programa en “funciones independientes” mediante ramas; el historial hace que uno use a ciegas unidades funcionales que pueden haber quedado obsoletas; y la colaboración suele cristalizar en la arquitectura del código fronteras organizacionales que no tienen relación. Esto también conecta con lo que dijo Mel Conway. Los beneficios del control de versiones son de sentido común, pero a nivel de “resolver el problema de negocio X” existen trade-offs reales. Es revelador que, a nivel de la industria, estos trade-offs casi no se vean.
    • En este caso, parece que lo que el autor quiso decir fue codificar lógica de versiones dentro de la propia app. Por ejemplo, endpoints de API por versión para compatibilidad hacia atrás.
  • Al principio pensé que el autor estaba totalmente equivocado, pero aun así hay algunas buenas ideas.
    Este flujo de trabajo le funciona muy bien al autor. La mayoría de nosotros también podemos recordar momentos en los que Git o las pruebas automatizadas nos frustraron o redujeron nuestra productividad. También hay soluciones más simples y menos intrusivas, como hacer backup del código con Dropbox, FTP, etc. La razón por la que el método anterior funciona bien es que el autor está optimizando su propia productividad en un proyecto personal de cariño en el que colabora con pocas personas. Las pruebas automatizadas son útiles, pero parece que al autor le gusta crear programas lo suficientemente pequeños como para que su valor sea difícil de ver. Incluso en este contexto creo que las pruebas automatizadas tienen valor, pero todos podemos estar de acuerdo en que ralentizan. Por supuesto, mucha gente dirá que la recompensa llega después. El control de versiones y las pruebas automatizadas resuelven problemas reales. Hoy no tiene sentido empezar un proyecto sin control de versiones, y hay razones por las que las pruebas automatizadas son una buena práctica. Pero en el caso de uso específico del autor, suena razonable. Si quitamos las partes polémicas sobre control de versiones y pruebas, los puntos 7/8/9 capturan perfectamente mi forma de pensar al escribir y refactorizar programas grandes: escribir, tirar y volver a escribir.

    • No estoy de acuerdo con lo del control de versiones. Lo mismo aplica aunque sea un proyecto individual y no haya varias ramas de versiones.
      La gente comete errores, y en un proyecto de más de 100 mil líneas ayuda mucho saber qué cambiaste en las últimas 3 semanas. Sirve para encontrar y corregir problemas. Una función aún mejor es que, con ramas, puedes probar lo que quieras manteniendo una forma de volver al estado estable anterior. Creo que se puede vivir sin pruebas automatizadas.
    • Incluso en un proyecto individual, vale totalmente la pena aprender suficiente Git como para configurar .gitignore y ejecutar git init, git add -A, git commit -a -m "before I changed the foo function to use bar", de modo que puedas volver a una revisión anterior.
      No hace falta dominar Git, pero solo tener mensajes de commit y versiones a las que volver me ha salvado incontables veces. Ni hablar de las funciones más avanzadas.
  • Es un texto bastante confuso. De verdad me da curiosidad por qué llegó al primer puesto.

    • Por un lado, podría ser el texto de un desarrollador que experimenta con distintas herramientas y técnicas para mejorar su vida. Por otro, también podría ser bait para empujar a la gente a discutir.
  • La principal motivación para contar con un conjunto razonable de pruebas es reducir la frustración. Un conjunto de pruebas le da al desarrollador confianza para hacer evolucionar el sistema.
    Si se hace bien, también es común terminar pensando algo como: “fue difícil encontrar una forma de probar el resto y, de todos modos, nos fue bastante bien”. A medida que crece la complejidad de las funcionalidades, la dificultad de probar componentes o el sistema completo puede volverse inmanejable. Pero la filosofía de abandonar las pruebas y el control de versiones para terminar con un mejor programa no escala más allá de una persona. Y aun así solo funciona cuando esa persona conoce todas las decisiones actuales y pasadas reflejadas en el código fuente como recuerdos recientes y cercanos. Además, si se conoce a fondo la implementación, toda validación de cambios, por definición, debe hacerse manualmente.

    • Hace tiempo vi en HN la historia de alguien que nunca hacía merge de código que no hubiera escrito él mismo ese día.
      Decía que, si al final del día no estaba en un estado apto para hacer merge, eso significaba que no había entendido el problema lo suficiente como para expresarlo en un día, así que a la mañana siguiente lo intentaba de nuevo desde cero. No sé si alguien recuerda esto, o si estoy confundiendo otro sitio o una anécdota distinta.
    • Como equipo de programación de una sola persona, tiene sentido. Sinceramente, incluso trabajando solo, me da miedo solo pensar en programar sin un conjunto de pruebas o control de versiones.
      La documentación, las pruebas y el control de versiones reducen la cantidad de contexto del código que tengo que recordar. Tengo que recordar los detalles del código que tengo enfrente, pero si lo documento, lo pruebo y lo registro con buenos mensajes de commit sobre por qué/cómo lo cambié, puedo sacarme ese código de la cabeza y pasar a lo siguiente.
  • Un buen ejemplo del punto 3 —“pequeños cambios en el contexto, como las personas/lugares/funciones que se busca apoyar, cambian drásticamente qué tan bien encaja un programa con ese contexto”— es K9 Mail. Ahora está en camino de convertirse en Thunderbird para Android.
    K9 Mail empezó con una UI poco tradicional que mostraba la lista de cuentas de correo en la pantalla de inicio e indicaba la cantidad de mensajes no leídos y el total de mensajes de cada cuenta. Tenía una bandeja de entrada unificada, pero no se la imponía al usuario. Recuerdo haber elegido explícitamente esta app porque quería mantener separadas una cuenta personal, una cuenta de trabajo y varias cuentas laborales que me habían dado clientes. Probablemente muchos usuarios de K9 la eligieron por la misma razón. Por eso hubo tantas quejas cuando el desarrollador pasó a una UI tradicional de Android, con la lista de cuentas deslizándose desde la izquierda y un toque adicional para moverse entre cuentas. Si nos hubiera gustado ese tipo de UI, probablemente no habríamos elegido K9 desde el principio. Así que un cambio pequeño —aunque seguramente implicó bastante código— arruinó la adecuación de la app para los usuarios a quienes les encajaba. Yo sigo usando 5.600, la última versión con la UI antigua, y la instalo por sideload cada vez que compro un dispositivo nuevo. Más particular todavía: para acceder a las cuentas uso solo POP3. Mi flujo es revisar desde el teléfono, borrar lo que haya que borrar, responder con copia oculta a mí mismo si hace falta y, finalmente, descargar todo en la laptop; K9 encajaba perfecto con ese workflow. No necesito nada sofisticado: una app al nivel de los 90 me alcanza.

  • Yo también sigo preguntándome a dónde llevará este camino. Algo está claro: hacer software en solitario es una actividad completamente distinta a hacerlo en equipo.
    Sobre las pruebas: las pruebas son un medio, no un fin. Creo que lo que buscamos es confianza. Si confiamos en la implementación, hacemos menos pruebas. En cambio, si hay algo que necesariamente debe seguir funcionando, agregamos algunas pruebas de integración en los límites externos, donde se ven menos afectadas por refactors y frenan menos el ritmo. Algo así como pinchar el backend web desde afuera, en vez de probar los componentes internos. Las pruebas unitarias son buenas para concretar el diseño de una API nueva, pero una vez que se entiende la dirección, esas pruebas se vuelven casi inútiles.

    • Hay muchísimas buenas razones para tener pruebas incluso en proyectos de una sola persona.
      Fijar temporalmente un if con algo como true || para ir directo a la funcionalidad que estás construyendo toma tiempo y luego hay que quitarlo. Es mejor crear una prueba y ejecutarla, y así queda como prueba de regresión. Si estás desplegando una app grande o lenta —a veces solo usar Qt ya hace que compilar o ejecutar tarde bastante—, una prueba individual puede cargar y correr más rápido. Si reproducir un bug toma 45 segundos, conviene escribir una prueba. Automatiza la parte más tediosa del trabajo, mantiene el flujo, te permite comprobar el estado del bug con la frecuencia que quieras sin tener que pensar cada vez si vale la pena hacerlo y, además, queda como prueba de regresión.
  • Me gusta mucho este autor, y Mu es uno de mis proyectos favoritos. Es algo parecido a una máquina Lisp moderna, y además un proyecto divertido que corre en QEMU.

  • Me gusta la frase: “la mayor parte del software está infectado de forma irremediable por incentivos para servir a mucha gente en el corto plazo”. Funciona igual si cambiamos software por “negocio”.

  • Todos estamos, en cierta medida, abrumados por la complejidad del campo de la ingeniería de software. A veces esa complejidad es accidental.
    Pero no estoy de acuerdo con que la solución sea rechazar todas las ideas que hemos desarrollado durante décadas. Por el contrario, tampoco hay que tomar todas las soluciones al pie de la letra ni usarlas “demasiado”. Estar abrumado, por definición, ocurre cuando se usa demasiado de algo. Escribe pruebas, usa sistemas de control de versiones, usa abstracciones, pero debes saber por qué las usas. Si ese “por qué” ya no se sostiene, hay que volver a evaluarlo.

    • Creo que una de las grandes fuentes del problema es la academia. Soy examinador externo de estudiantes de CS en Dinamarca, y todavía aprenden a construir abstracciones upfront con orientación a objetos y arquitectura cebolla.
      Eso es casi uno de los peores hechizos en el desarrollo de software. Lo peor es que estas cosas se aprenden casi como una religión. Lo extraño es que, en los últimos años, la forma en que los profesionales escriben software ha evolucionado mucho. Como dije, la abstracción no es intrínsecamente mala para todo. Incluso cuesta imaginar no tener una clase base que incluya campos como updated y updated_by para los datos típicos que van en una base de datos SQL. Pero, en general, casi no uso abstracciones salvo que realmente me vea obligado. En cambio, en la academia todavía enseñan exactamente el mismo currículo que aprendí hace 25 años. Se siente muy raro evaluar a estudiantes por su capacidad de crear enormes abstracciones con UML elegante e implementarlas en código. El 90% de ellos probablemente nunca volverá a ver ni un solo diagrama UML. Al menos así será en el pequeño ámbito en el que me muevo. Aun así, la realidad es la que es.
    • La única razón por la que realmente empecé a usar Git fue magit.
      Me gustaría que todo tuviera una “porcelana” a nivel de línea de comandos. Creo que con una salida estándar --help=ui y una interfaz estilo dialog se podría automatizar. Más que estar abrumado por la complejidad, el problema es que hay un límite a la cantidad de memoria muscular activa que uno puede aprovechar, y en algún punto hay que recortar.