Atención al efecto Makefile
(blog.yossarian.net)- El efecto Makefile se refiere al patrón de no usar desde cero herramientas complejas o poco familiares, sino copiar una configuración que funcionó bien antes y adaptarla a una nueva situación
- En la etapa de resolución de problemas, reutilizar ejemplos verificados puede ser una respuesta de ingeniería razonable, porque reduce el riesgo de bugs
- Pero, desde la perspectiva del diseño, puede verse como una señal de usabilidad: la herramienta es difícil o molesta de usar desde cero, por lo que los cambios se van acumulando en copias
- Flujos similares de copiar y modificar aparecen no solo en Make, sino también en GitHub Actions, GitLab CI/CD, configuraciones de linters y formatters, y sistemas de build
- Quienes diseñan herramientas deberían revisar si la configuración y la sintaxis propia son realmente necesarias, si pueden reutilizar convenciones familiares de CLI y si los usuarios terminan dependiendo de copiar y pegar
Qué es el efecto Makefile
- El efecto Makefile es el fenómeno de no escribir desde cero una herramienta con cierto nivel de complejidad o falta de familiaridad, sino copiar y modificar un ejemplo anterior que “funcionó bien”
- El flujo típico es el siguiente
- Hay que completar una tarea común, y una tarea muy similar o idéntica ya se realizó antes
- Make o una herramienta parecida se elige como la herramienta adecuada para esa tarea
- En lugar de escribir un nuevo
Makefile, un ingeniero copia elMakefilede un trabajo anterior y lo ajusta hasta que funcione en el nuevo entorno
- Make es solo el caso representativo; como cada equipo o persona domina herramientas distintas, algunas familias de herramientas son más propensas a este efecto y otras relativamente menos
- El chiste de un “
Makefileheredado” se refiere a la situación en la que unMakefilerecibido de un ingeniero senior, un profesor, etc., se sigue transmitiendo con apenas pequeñas modificaciones
Problemas de diseño de herramientas que revela la copia repetida
- Cuando hay que resolver un problema de inmediato, reutilizar un ejemplo que funciona puede ser una opción simple
- Como la mayor parte del trabajo no cambia, en teoría se pueden introducir menos bugs nuevos
- El efecto Makefile no es, en esencia, un fenómeno malo ni ineficiente
- En el momento de diseño, se vuelve una señal de que la herramienta, o la forma de aplicarla, es demasiado compleja o engorrosa
- Los usuarios copian una y otra vez soluciones existentes en vez de resolver el problema desde cero
- Si este efecto aparece en casos de uso simples, la herramienta puede ser demasiado compleja para ese propósito
- Este patrón también aparece en herramientas centradas en configuración fuera de Make
- En configuraciones de CI/CD, se copia el YAML de GitHub Actions o GitLab CI/CD desde la última configuración que funcionó, se ejecuta repetidamente y se corrige
- Las configuraciones de linters y formatters copian entre proyectos un conjunto de reglas base, y luego lo endurecen o relajan según las condiciones locales
- Las configuraciones no triviales de sistemas de build tienden a parecerse a sistemas de build anteriores
Costos en aprendizaje, debugging y seguridad
- Las herramientas donde el efecto Makefile es fuerte pueden obstaculizar un aprendizaje amplio
- Un pequeño grupo de personas expertas configura la herramienta, y el resto copia y modifica entendiendo solo las partes necesarias
- El grafo de dependencias de un sistema de build tiene una complejidad inherente, pero tener que recordar la diferencia entre
$<y$^en Make no la tiene
- Cuando falta soporte de diagnóstico y debugging, los usuarios terminan dependiendo de ejecutar la herramienta muchas veces y obtener solo pequeñas pistas
- En CI/CD, se termina haciendo debugging estilo print atravesando capas de red y orquestación de VMs intermedias
- También pueden aumentar los riesgos de seguridad
- Las medidas de seguridad suelen requerir entender en profundidad el “por qué” del comportamiento
- Los sistemas donde aparece el efecto Makefile a menudo permiten confusiones entre código y datos, o confusiones generales de señalización en banda (in-band signaling)
- La inyección de plantillas de GitHub Actions es un ejemplo representativo
Preguntas que deben hacerse quienes diseñan herramientas
- Al crear una herramienta nueva, hay que observar si los usuarios pueden escribirla desde cero o si solo pueden buscar y copiar un “ejemplo que funciona”
- Estas son algunas preguntas de revisión
- ¿La configurabilidad es realmente necesaria?
- ¿Hace falta una sintaxis propia?
- ¿Se pueden reutilizar la sintaxis y las convenciones familiares de otras herramientas o de la CLI?
- ¿Incluso quien la creó termina copiando y pegando su uso? Si es así, es probable que otros usuarios también lo hagan
- Sobre el término “cargo cult”, el artículo de Ken Shirriff aborda el problema de que es una jerga técnica inapropiada
1 comentarios
Opiniones de Hacker News
“Invariablemente se descubre que un sistema complejo que funciona evolucionó a partir de un sistema simple que funcionaba. Un sistema complejo diseñado desde cero nunca funciona, y no se puede parchar para que funcione. Hay que volver a empezar con un sistema simple que funcione”
— John Gall (1975), Systemantics: How Systems Really Work and How They Fail
https://en.wikipedia.org/wiki/John_Gall_(author)#Gall's_law
Normalmente empiezan a pegarles funcionalidades como parches para manejar casos especiales, y eso parece una señal de que esas excepciones seguirán multiplicándose sin fin.
Hay una diferencia sutil pero importante entre “aplicar la experiencia” y “diseñar un sistema completamente nuevo alrededor de una sola cosa que a uno le molesta personalmente”.
Tengo otra hipótesis. Alrededor del 10% de los desarrolladores realmente entiende los principios y puede empezar algo desde cero, y otro 40% puede ocuparse del trabajo diario copiando y pegando desde código local, Stack Overflow, GitHub y LLM, entendiendo más o menos qué está pasando.
El 50% restante no sabe mucho más allá de algunos acertijos de LeetCode, y tampoco tiene una comprensión real de lo que copia y pega.
Con esa distribución, es probable que más de la mitad de los Makefile sean fragmentos copiados y pegados que funcionan más o menos. Si lo trajeron de algún lugar donde ya funcionaba, entonces el trabajo queda hecho por ahora y pasan al siguiente ticket.
No estoy culpando a la herramienta en sí. Makefile es bien conocido y no es verboso en proyectos pequeños. Puede ser una mala elección para un monstruo de 10,000 archivos, pero también he visto Makefile limpios en proyectos enormes. Personalmente no sería mi primera opción, pero me gustan los Makefile y los he usado de forma intermitente durante más de 30 años.
Por ejemplo, cuando un programador de C++ trabaja en un ticket, ¿hasta cuántas capas del stack necesita conocer? ¿Tiene que saber los nombres de los registros de la CPU? ¿Qué necesita saber un investigador de IA que solo usa Jupyter? Es recomendable aprender lo máximo posible sobre las herramientas y el stack, pero el tiempo es limitado.
Si en un Makefile veo reglas individuales para construir un único archivo, casi siempre lo considero un error.
Normalmente solo debería haber reglas generales de build para construir todos los archivos de un tipo determinado.
Un Makefile casi nunca debería contener listas de archivos fuente ni listas de dependencias; debería bastar con una lista de directorios donde están los archivos fuente.
Make debería buscar en los directorios fuente para encontrar archivos, clasificarlos por tipo, crear la lista de dependencias y llamar a la regla de build adecuada. Al menos en GNU make es muy sencillo y está en el manual de usuario.
Escrito así, el esfuerzo de crear o modificar el Makefile es igual de insignificante tanto si el proyecto tiene 1 archivo como si tiene 10,000. Tampoco hace falta actualizar el Makefile cada vez que se crea, renombra, mueve o elimina un archivo fuente.
Es porque cada lenguaje y framework tiene un “orden” de build distinto y no puedo recordarlos todos. Pero
$ makees fácil.Estoy de acuerdo en que muchos desarrolladores de apps productivos quizá no puedan configurar un proyecto nuevo desde cero, pero muchas veces eso es más una cuestión de conocer las reglas mágicas y los conjuros correctos para hacer encajar varias herramientas que de tener una comprensión profunda.
Otro factor que he visto en el trabajo son las herramientas y sistemas que los desarrolladores tendrán que manejar algún día, pero que normalmente sienten que tienen poco valor aprender
Por ejemplo, los sistemas de build y la configuración de CI. Son indispensables, pero los desarrolladores no creen que deban lidiar con ellos todos los días. CI se ve como un sistema de “configurar y olvidar”, y la reacción es: ¿de verdad tengo que aprender todo esto solo para compilar la app?
Los desarrolladores esperan que “simplemente funcione”, y si hay complejidad, asumen que la maneja otro equipo, alguien con un rol como el mío. Por eso, cada vez que un desarrollador toca este sistema, tiene un fuerte incentivo a copiar la última configuración que funcionaba, pasarla adelante y volver al trabajo “de verdad”
La mejor solución es encontrarse con los desarrolladores a medio camino. Hay que ofrecer herramientas tan simples o complejas como requiera la tarea, tener documentación y minimizar la creencia de que es “magia”. Herramientas como Make suelen fallar en este punto porque se sienten demasiado complejas y como una caja negra
Por eso el ciclo de retroalimentación se vuelve larguísimo y el trabajo en sí se vuelve muy doloroso
Si un desarrollador puede ejecutar localmente parte o la totalidad del trabajo de “CI/CD”, puede verlo, controlarlo y entenderlo. Eso ayuda muchísimo a dar sensación de propiedad y seguridad, en lugar de sentir que “CI/CD es cosa de otro mundo”
No siempre funciona. Una vez un equipo de dos personas tenía CI/CD con un wrapper delgado y aun así lo trataba como un proceso alienígena y se negaba a tocarlo
Del mismo modo, CI necesita propiedad, y hacer que la responsabilidad recaiga en el último desarrollador que intentó usar CI no es una forma eficiente de trabajar
La solución es no adoptar herramientas que usan las grandes empresas solo porque las usan las grandes empresas. Será una opinión impopular, pero creo que CI/CD no hace falta en la mayoría de los lugares donde se usa
Aunque implique algunos pasos más, hay que encontrar una forma de hacer build y despliegue minimizando absolutamente las partes móviles. Después hay que evaluar con cuidado el costo de qué partes automatizar. Incorporar un sistema grande para hacer algo simple normalmente no vale la pena a largo plazo
Si de verdad necesitas CI/CD, lo vas a saber porque habrá un punto de dolor. Si ese sistema les causa dolor a los desarrolladores, no es la herramienta adecuada
makete parece “demasiado complejo y como una caja negra”, es porque todavía no vistecmakeMake y los Makefile son increíblemente simples cuando no los generó automáticamente autoconf. Si los generó autoconf, no deberías modificarlos: son un artefacto de build. Si es posible, también conviene deshacerse de autoconf
En términos más amplios, este efecto realmente existe. Puedes ser víctima de él o aprovecharlo. La forma de aprovecharlo es esta: escribes un poco de código o lo copias y pegas de algún lado, lo usas en un proyecto y lo ajustas según haga falta. Cuando empiezas el siguiente proyecto, vuelves a copiar ese código y lo modificas para el segundo proyecto. Ves si puedes aplicar esos cambios de vuelta al proyecto original. Si ambos funcionan y están sincronizados, extraes ese código y lo conviertes en una biblioteca. A veces hacen falta más proyectos para refinarlo hasta que tenga forma de biblioteca. En el mejor de los casos, lo publicas como open source para que otros también lo usen
Como Git no registra timestamps internamente, el mtime de un archivo puede actualizarse aunque el contenido sea el mismo, y de hecho eso pasa a menudo, lo que provoca rebuilds innecesarios
Tampoco maneja bien en absoluto herramientas modernas cuyas entradas o salidas son directorios completos, ni herramientas cuyo nombre de salida no se conoce antes de ejecutarlas
Me gusta make, lo he usado bien a pesar de sus desventajas y conozco los workarounds y hasta los workarounds de esos workarounds. Pero cuando una herramienta empieza a no encajar perfectamente con las expectativas de make, la dificultad y la complejidad de escribir un Makefile correcto se disparan rápidamente
No es broma. El hecho mismo de que exista el concepto de
make cleanes una prueba concluyenteMakefile quizá sea una analogía equivocada. El problema que la mayoría de la gente tiene con los Makefile es que los escribe muy rara vez. Tienen una idea general de lo que hace make, pero no tienen la memoria muscular para escribirlos desde cero
Aun así, el punto es correcto. He visto mucho código copiado de la web con un montón de código muerto que en realidad no se usa. Una buena práctica es seguir borrando hasta que algo se rompa, y revertir solo la parte que se rompió. Hay que borrar todo lo posible, y definitivamente todo lo que no se esté usando actualmente
Mi solución hasta ahora ha sido crear un repositorio separado con notas y programas de ejemplo en scripts de shell sobre estas herramientas, bibliotecas y frameworks
Cada vez que necesito volver a usar una herramienta, consulto las notas para refrescar la memoria, y si aprendo algo nuevo, las actualizo. Ahora también puedo apuntar un LLM a ese repositorio y hacerle preguntas
Ese repositorio es personal y contiene información sobre herramientas disponibles públicamente. El conocimiento específico de una organización lo guardo en un repositorio similar pero separado, y lo destruyo cuando termina el contrato con el cliente o el empleador
La mejor forma de llamar a esto es desarrollo de culto cargo. El culto cargo surgió en el Pacífico durante la Segunda Guerra Mundial, cuando los isleños nativos vieron aviones casi milagrosos que traían comida, alcohol y suministros, y empezaron a imitar lo que hacían los soldados, rezando para que aviones de bambú y dispositivos de coco impresionaran a los dioses y los suministros volvieran a llegar.
El problema era que los isleños no entendían la ciencia detrás de cosas como aviones, radios y armas.
De la misma manera, el desarrollador de culto cargo ve los posibles resultados, pero no entiende los primeros principios; imita las acciones de los sumos sacerdotes de la tecnología con la esperanza de poder copiar el éxito.
Así termina repitiendo copiar, pegar, probar, toquetear, buscar, jalar, empujar y ajustar, esperando que esta vez más o menos funcione, aunque sea con ciertos datos de un martes por la noche.
En la práctica, consiste en repetir acciones o rituales que por sí mismos no tienen efecto, esperando obtener los resultados que obtuvo otra empresa que realizó los mismos rituales.
Lo que dice el artículo original es distinto. Lo que se copia, el código, sí tiene efecto por sí mismo. Se puede probar y determinar si funciona.
Esta distinción es importante. Como los síntomas del efecto Makefile del artículo original y los del culto cargo son distintos, tratarlos como si fueran lo mismo dificulta el diagnóstico. En el culto cargo se pierde tiempo, por superstición, en cosas que en realidad no funcionan. En el efecto Makefile, algo que se ha demostrado que funciona se copia junto con fragmentos parecidos a órganos vestigiales, y con el tiempo se vuelve cada vez más difícil de mantener.
En concreto, no creo que esto sea exactamente igual al culto cargo. El culto cargo implica falta de comprensión. Pero uno puede entender bien un sistema y aun así vivir principalmente copiando y pegando, porque la complejidad intrínseca de ese sistema incentiva hacerlo. Ese era el punto central del artículo.
Normalmente entiendo lo que ocurre por dentro, pero entrar en ese “modo” es cognitivamente pesado, así que lo evito cuando puedo.
Las herramientas que caen en esta categoría suelen ser herramientas de operaciones, como CI/CD, k8s y Docker: tienen una complejidad enorme, pero no son el problema central que estoy resolviendo. Make, en particular, hace difícil evitar el cambio de contexto, así que últimamente simplemente lo evito.
No tiene que ver con conjuros mágicos; sé cuál es el compromiso que estoy haciendo. Aun así, existe el riesgo de que se convierta en el efecto Makefile.
“El efecto Makefile se parece a otros fenómenos, como el culto cargo, la normalización de la desviación y los ‘lenguajes de solo escritura’. En este artículo sostendré que difiere un poco de cada uno en que no es inherentemente ineficiente ni malo, sino que aborda el resultado de ciertos diseños”.
Entonces, si alguien de verdad quiere “salir de la isla y pilotar el avión”, ¿eso significa que, al final, la universidad es el camino más cercano a lo “real”?
Sé que es difícil hablar en términos absolutos, que hay muchas excepciones y que cada persona tiene distintas formas de justificar “yo no puedo hacer eso”. Aquí lo pregunto desde el punto de vista de una teoría óptima. El camino óptimo para evitar conductas de culto es entender el todo, y ¿ese “todo” no viene de la educación superior?
Lógicamente, aunque uno estudie con seriedad a partir de libros e intente igualar o superar la “integridad” de la universidad, en ciertos aspectos parece que quedaría por debajo del tiempo pasado con docentes conocedores y en un aula. Incluso suponiendo que la misma persona se esfuerce igual en ambos caminos.
Otro factor es la frecuencia de uso. Escribo textos grandes en LaTeX una vez al año o menos.
El nivel de LaTeX que uso no es el de una herramienta difícil, pero si solo la uso unas semanas al año, no hay forma de recordar los detalles de uso, así que normalmente empiezo un documento nuevo copiando y pegando uno anterior.
Pero con LaTeX, aunque antes lo usé muchísimo, siempre copiaba y pegaba desde una plantilla. Las presentaciones en beamer y los diagramas en TikZ eran aún peor: copiaba desde presentaciones o diagramas anteriores en vez de desde una plantilla.
TikZ me parece una herramienta intrínsecamente compleja y creo que no la aprendí lo suficiente.
LaTeX creo que sí lo aprendí lo suficiente, así que puede haber otra razón.
En mi opinión, puede ser un problema de valores predeterminados razonables. Una buena herramienta debería ofrecer buenos valores predeterminados. Pero LaTeX no es una buena herramienta según ese criterio. Mis documentos suelen empezar más o menos así: La mayor parte de esto sirve para obtener soporte básico no ASCII necesario para mi idioma nativo, o para activar valores predeterminados razonables como papel A4 y microtype. Con herramientas modernas como pandoc/Markdown, quizá no haría falta.
Así que muchas veces el objetivo de copiar y pegar estas cosas es obtener buenos valores predeterminados que una mejor herramienta habría ofrecido desde el inicio.
Si no vas a escribir sin matemáticas o solo texto puro en inglés, LaTeX es casi como “baterías no incluidas”.
Esto es desarrollo impulsado por copiar y pegar [0], y tampoco es algo relacionado solo con Makefile. Tiene que ver con que toda la industria copia código de un lado a otro sin saber siquiera qué está copiando
Sinceramente, creo que Copilot empeoró esto, porque estamos aceptando bloques de código en las bases de código con los ojos cerrados
[0] https://andrew.grahamyooll.com/blog/copy-pasta-driven-develo...
makeprobablemente al menos 7 veces seguidas, pero nunca me dieron tiempo para lidiar con eso todos los días hasta memorizarlo por completoEn algún momento simplemente me rendí. Se volvió un conocimiento arcano y esotérico con el que no podía desarrollar memoria muscular, y que tenía que volver a aprender desde cero cada vez que lo necesitaba. Así que me pasé a herramientas más simples
La pérdida del trabajo profundo no es culpa de los buenos programadores, sino de la gente del lado de negocio
He visto demasiadas veces cómo copian tal cual un Makefile de otro proyecto y luego lo modifican para que “funcione”, dejando intactos pasos y targets de build innecesarios que no tienen ninguna relación
Personalmente, eso me molesta mucho
¿Por prueba y error? Entonces, que te diviertas :p
Es cierto que hay muchas herramientas más complejas de lo necesario, pero no creo que Make en sí sea un buen ejemplo de eso
En las herramientas modernas, el problema de la complejidad suele empeorar por documentación deficiente, documentación tipo recetario, documentación que no explica el modelo conceptual necesario para razonar sobre cómo funciona la herramienta, o documentación sin especificaciones suficientemente detalladas y precisas
Make no es así. Está bien documentado y, si realmente te tomas el tiempo de leer la documentación, no es difícil de entender
El enfoque tipo recetario mencionado arriba lleva a una cultura que subestima la importancia de aprender y entender las herramientas que uno usa, y de la documentación exhaustiva que lo hace posible. O quizá la causalidad sea al revés
En cualquier caso, creo que el problema es que dedicamos muy poco tiempo a crear documentación de herramientas que sea completa y esté actualizada, que no diseñamos lo suficiente las herramientas para que esa documentación sea posible desde el principio, y que asignamos muy pocos recursos a enseñar y aprender las herramientas necesarias