Alerta de cautela com o efeito Makefile
(blog.yossarian.net)- O efeito Makefile se refere ao padrão de não usar do zero uma ferramenta complexa ou pouco familiar, mas copiar uma configuração que já funcionou antes e ajustá-la para uma nova situação
- Na etapa de resolver um problema, reutilizar exemplos já validados pode ser uma resposta de engenharia racional para reduzir o risco de bugs
- Mas, do ponto de vista de design, também pode ser visto como um sinal de usabilidade de que a ferramenta é difícil ou incômoda de usar desde o início, levando ao acúmulo contínuo de mudanças sobre cópias
- Além do Make, um fluxo parecido de copiar e ajustar aparece em GitHub Actions, GitLab CI/CD, configurações de linter e formatter, e também em sistemas de build
- Quem projeta ferramentas deve verificar se configuração e sintaxe própria são realmente necessárias, se é possível reutilizar convenções familiares de CLI e se os usuários acabam dependendo de copiar e colar
O que é o efeito Makefile
- O efeito Makefile é o fenômeno de, em vez de escrever algo novo para uma ferramenta com certo nível de complexidade ou estranheza, copiar e modificar um exemplo anterior que “já funcionou”
- O fluxo típico é o seguinte
- É preciso concluir uma tarefa de um tipo comum, e uma tarefa muito parecida ou idêntica já foi feita antes
- Make ou uma ferramenta semelhante é escolhida como a ferramenta adequada para essa tarefa
- Em vez de escrever um novo
Makefile, a pessoa engenheira copia oMakefiledo trabalho anterior e o corrige até que funcione no novo ambiente
- O Make é apenas o caso mais representativo; como cada equipe ou pessoa domina ferramentas diferentes, alguns grupos de ferramentas são mais vulneráveis a esse efeito e outros relativamente menos
- A piada sobre um “heritage
Makefile” se refere à situação em que umMakefileherdado de um engenheiro sênior, professor ou outra fonte continua sendo transmitido com apenas pequenas alterações
Problemas de design de ferramentas revelados pela cópia repetida
- Quando é preciso resolver o problema imediatamente, reutilizar um exemplo que funciona pode ser uma escolha concisa
- Como a maior parte da tarefa não muda, em teoria isso pode introduzir menos bugs novos
- O efeito Makefile não é, em si, um fenômeno necessariamente ruim ou ineficiente
- No momento do design, porém, ele se torna um sinal de que a ferramenta — ou a forma como ela é aplicada — é complexa ou trabalhosa demais
- Em vez de resolver o problema do zero, os usuários repetem a cópia de soluções existentes
- Se esse efeito aparece em casos simples de uso, a ferramenta pode estar excessivamente complexa para esse propósito
- Esse padrão também aparece em ferramentas baseadas em configuração além do Make
- Em configurações de CI/CD, YAMLs de GitHub Actions ou GitLab CI/CD costumam ser copiados da última configuração que funcionou e ajustados por tentativas sucessivas
- Configurações de linter e formatter copiam conjuntos básicos de regras entre projetos e depois os endurecem ou flexibilizam conforme as condições locais
- Configurações não triviais de sistemas de build tendem facilmente a se parecer com o sistema de build anterior
Custos em aprendizado, depuração e segurança
- Ferramentas em que o efeito Makefile é forte podem atrapalhar um aprendizado mais amplo
- Um pequeno grupo de especialistas configura a ferramenta, enquanto o restante apenas copia e ajusta entendendo só as partes necessárias
- O grafo de dependências de um sistema de build é uma complexidade essencial, mas ter de lembrar a diferença entre
$<e$^no Make não é
- Quando faltam recursos de diagnóstico e suporte à depuração, os usuários acabam dependendo de executar a ferramenta repetidas vezes e obter apenas pequenas pistas
- Em CI/CD, isso leva a depuração no estilo
print, passando por rede e por camadas intermediárias de orquestração de VM
- Em CI/CD, isso leva a depuração no estilo
- Do ponto de vista de segurança, os riscos também podem aumentar
- Medidas de segurança muitas vezes exigem compreensão profunda do “porquê” de um comportamento
- Sistemas em que o efeito Makefile aparece com frequência costumam permitir confusão entre código e dados, ou uma confusão mais geral de sinalização em banda (in-band signaling)
- Um exemplo representativo é o template injection do GitHub Actions
Perguntas que designers de ferramentas devem fazer
- Ao criar uma nova ferramenta, é preciso observar se os usuários conseguem escrever algo do zero ou se precisam necessariamente encontrar um “exemplo que funciona” para copiar
- As perguntas a verificar são as seguintes
- A possibilidade de configuração é realmente necessária?
- Uma sintaxe própria é realmente indispensável?
- É possível reutilizar sintaxe e convenções familiares de outras ferramentas ou da CLI?
- Até mesmo quem criou a ferramenta acaba copiando e colando seu modo de uso? Se sim, há grande chance de outros usuários fazerem o mesmo
- Sobre o termo “cargo cult”, o texto de Ken Shirriff discute o problema de ele ser um jargão inadequado
1 comentários
Opiniões no Hacker News
“Um sistema complexo que funciona invariavelmente se revela como tendo evoluído de um sistema simples que funcionava. Um sistema complexo projetado do zero nunca funciona e não pode ser remendado para funcionar. É preciso recomeçar com um sistema simples 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
Em geral, começam a surgir recursos meio remendados para lidar com casos especiais, e isso parece um sinal de que essas exceções vão continuar se multiplicando sem fim
Há uma diferença sutil, mas importante, entre “aplicar experiência” e “projetar um sistema totalmente novo em torno de uma única coisa que incomoda pessoalmente”
Tenho outra hipótese. Cerca de 10% dos desenvolvedores realmente entendem os princípios e conseguem começar algo do zero, e outros 40% conseguem lidar com o trabalho do dia a dia copiando e colando de código local, Stack Overflow, GitHub e LLMs, tendo uma noção aproximada do que está acontecendo
Os 50% restantes não sabem muito além de alguns quebra-cabeças do LeetCode e também têm pouca compreensão real do que estão copiando e colando
Com essa distribuição, é bem provável que mais da metade dos Makefiles seja composta de trechos copiados e colados que funcionam mais ou menos. Se veio de um lugar onde já funcionava, o trabalho está resolvido por ora e dá para passar para o próximo ticket
Não estou culpando a ferramenta em si. Makefiles são bem conhecidos e, em projetos pequenos, nem são verbosos. Podem ser uma escolha ruim para um monstro de 10.000 arquivos, mas já vi Makefiles limpos até em projetos enormes. Pessoalmente, não seriam minha primeira escolha, mas gosto de Makefiles e os uso de forma intermitente há mais de 30 anos
Por exemplo, ao cuidar de um ticket, até quantas camadas da stack um programador C++ precisa conhecer? Precisa saber até os nomes dos registradores da CPU? Até onde um pesquisador de IA que só usa Jupyter precisa saber? É recomendável aprender o máximo possível sobre as ferramentas e a stack, mas o tempo é limitado
Quando vejo em um Makefile uma regra individual para criar um único arquivo, quase sempre considero isso um erro
Normalmente, deveria haver apenas regras gerais de build usadas para compilar todos os arquivos de um determinado tipo
O Makefile quase nunca deveria conter uma lista de arquivos-fonte ou uma lista de dependências; bastaria ter a lista de diretórios onde estão os arquivos-fonte
O Make deve procurar nos diretórios de código-fonte para encontrar os arquivos, classificá-los por tipo, criar a lista de dependências e chamar as regras de build adequadas. Pelo menos no GNU make, isso é muito simples e está no manual do usuário
Escrevendo assim, o esforço para criar ou modificar o Makefile é igualmente desprezível, seja o projeto de 1 arquivo ou de 10.000. Também não é preciso atualizar o Makefile toda vez que um arquivo-fonte é criado, renomeado, movido ou excluído
É porque cada linguagem e framework tem uma “sequência” de build diferente, e não dá para lembrar todas. Mas
$ makeé fácilConcordo que muitos desenvolvedores de apps produtivos talvez não consigam configurar um projeto novo do zero, mas muitas vezes isso é mais uma questão de conhecer as regras mágicas e os encantamentos certos para encaixar várias ferramentas do que de ter uma compreensão profunda
Outro fator que vi no trabalho são ferramentas e sistemas com que os desenvolvedores acabam tendo de lidar algum dia, mas que no dia a dia sentem que têm pouco valor em aprender
Por exemplo, sistemas de build e configurações de CI. São indispensáveis, mas os desenvolvedores não acham que deveriam ter de lidar com isso todos os dias. CI é vista como um sistema de “configurar e esquecer”, e a reação é: será que preciso aprender tudo isso só para compilar o app?
Os desenvolvedores esperam que “simplesmente funcione” e, se houver complexidade, entendem que outro time — alguém como eu, no meu papel — vai cuidar dela. Por isso, sempre que um desenvolvedor mexe nesse sistema, há uma forte motivação para copiar a última configuração que funcionava, passar adiante e voltar ao trabalho “de verdade”
A melhor solução é encontrar os desenvolvedores no meio do caminho. É preciso oferecer ferramentas simples ou complexas conforme a tarefa, ter documentação e minimizar a crença de que é “mágica”. Ferramentas como Make costumam falhar nesse ponto porque parecem complexas demais e uma caixa-preta
Com isso, o ciclo de feedback fica extremamente longo, e o trabalho em si vira um grande sofrimento
Se o desenvolvedor consegue executar localmente parte ou a totalidade das tarefas de “CI/CD”, ele consegue ver, controlar e entender. Isso ajuda muito a dar senso de propriedade e segurança, em vez da sensação de que “CI/CD é coisa de outro mundo”
Nem sempre funciona. Já vi uma equipe de duas pessoas que, mesmo com um CI/CD de wrapper fino, tratava aquilo como um processo alienígena e se recusava a mexer
Da mesma forma, CI precisa ter ownership, e deixar a responsabilidade cair sobre o último desenvolvedor que tentou usar o CI não é uma forma eficiente de trabalhar
A solução é não adotar ferramentas que grandes empresas usam só porque grandes empresas as usam. Vai ser uma opinião impopular, mas acho que CI/CD não é necessário na maioria dos lugares onde é usado
Mesmo que o processo ganhe algumas etapas, é preciso encontrar uma forma de fazer build e deploy minimizando ao máximo absoluto as partes móveis. Depois, avaliar com cuidado o custo de automatizar cada parte. Trazer um sistema grande para fazer algo simples normalmente não vale a pena no longo prazo
Se você realmente precisar de CI/CD, vai saber porque haverá um ponto de dor. Se esse sistema causa dor aos desenvolvedores, não é a ferramenta certa
makeparece “complexo demais e uma caixa-preta”, é porque você ainda não viucmakeMake e Makefile são extremamente simples quando não são gerados automaticamente pelo autoconf. Se foram gerados pelo autoconf, não devem ser editados; são artefatos de build. Se possível, é melhor abandonar também o autoconf
Em termos mais amplos, esse efeito existe de fato. Você pode ser vítima dele ou pode aproveitá-lo. A forma de aproveitar é esta: escrever um pouco de código, ou copiar e colar de algum lugar, usar no projeto e ajustar conforme necessário. Ao iniciar o próximo projeto, copiar esse código de novo e modificá-lo para o segundo projeto. Ver se as mudanças podem ser aplicadas de volta ao projeto original. Quando ambos funcionarem e estiverem sincronizados, extrair esse código e transformá-lo em uma biblioteca. Às vezes são necessários mais projetos para refiná-lo até ficar com cara de biblioteca. No melhor dos casos, publicá-lo como open source para que outras pessoas também usem
Como o Git não registra timestamps internamente, o mtime de um arquivo pode ser atualizado mesmo quando o conteúdo é igual — e isso de fato acontece com frequência —, causando rebuilds desnecessários
Ele também não lida direito com ferramentas modernas em que a entrada ou a saída é um diretório inteiro, nem com ferramentas cujos nomes de saída não são conhecidos antes da execução
Gosto de make, usei bem apesar das limitações e conheço os workarounds e até os workarounds dos workarounds. Mas, quando as ferramentas começam a deixar de se encaixar perfeitamente nas expectativas do make, a dificuldade e a complexidade de criar um Makefile correto disparam rapidamente
Não é brincadeira. A própria existência do conceito de
make cleané uma prova decisiva dissoMakefile talvez seja uma analogia ruim. O problema que a maioria das pessoas enfrenta com Makefile é que elas o escrevem raramente demais. Têm uma noção geral do que o make faz, mas não têm memória muscular para escrever um do zero
Ainda assim, a ideia principal está certa. Já vi muito código copiado da web cheio de código morto que na prática não é usado. Um bom hábito é continuar apagando até quebrar, e então restaurar apenas a parte que quebrou. É preciso apagar o máximo possível, e com certeza apagar tudo o que não está sendo usado no momento
A solução até agora tem sido manter um repositório separado com anotações sobre essas ferramentas, bibliotecas e frameworks, além de programas de exemplo em shell script
Sempre que preciso usar uma ferramenta de novo, consulto as anotações para refrescar a memória e atualizo as notas quando aprendo algo novo. Agora também posso apontar um LLM para esse repositório e fazer perguntas
Esse repositório é pessoal e contém informações sobre ferramentas disponíveis publicamente. Conhecimento específico de uma organização fica em um repositório semelhante, mas separado, e é descartado quando termina o contrato com um cliente ou empregador
O melhor nome para isso é desenvolvimento por culto à carga. O culto à carga surgiu no Pacífico durante a Segunda Guerra Mundial: ilhéus nativos viram aviões quase milagrosos trazendo comida, bebida e suprimentos e passaram a imitar o comportamento dos soldados, fazendo aviões de bambu e dispositivos de coco na esperança de comover os deuses para que os suprimentos voltassem
O problema era que os ilhéus não entendiam a ciência por trás de coisas como aviões, rádios e armas
Da mesma forma, o desenvolvedor de culto à carga vê os resultados possíveis, mas não entende os primeiros princípios; imita o comportamento dos sumos sacerdotes da tecnologia esperando conseguir copiar o sucesso
Então fica repetindo copiar, colar, tentar, mexer, pesquisar, puxar, empurrar e ajustar, esperando que desta vez funcione mais ou menos, nem que seja para aqueles dados específicos de uma noite de terça-feira
Na prática, é repetir ações ou rituais que não têm efeito por si só, esperando obter os resultados que outra empresa obteve ao realizar o mesmo ritual
O que o texto original descreve é diferente. O objeto copiado, o código, tem efeito por si só. Dá para testá-lo e determinar se funciona
Essa distinção é importante. Como os sintomas do efeito Makefile do texto original e do culto à carga são diferentes, tratá-los como a mesma coisa dificulta o diagnóstico. No culto à carga, perde-se tempo em coisas que de fato não funcionam, por superstição. No efeito Makefile, o funcionamento é comprovado, mas pedaços como órgãos vestigiais vão sendo copiados, tornando a manutenção cada vez mais difícil
Mais especificamente, não acho que isso seja exatamente o mesmo que culto à carga. Culto à carga implica falta de entendimento. Mas é possível entender bem um sistema e, ainda assim, viver principalmente de copiar e colar porque a complexidade intrínseca desse sistema incentiva isso. Esse era o ponto central do texto
Em geral entendo o que está acontecendo por dentro, mas mudar para esse “modo” em si é cognitivamente pesado, então evito quando posso
As ferramentas que entram nessa categoria costumam ser ferramentas operacionais como CI/CD, k8s e Docker, com uma complexidade enorme, mas que não são o problema central que estou resolvendo. Make, em especial, dificulta evitar a troca de contexto, então hoje em dia eu simplesmente costumo evitá-lo
Não tem nada a ver com encantamentos mágicos; eu sei qual concessão estou fazendo. Ainda assim, há o risco de isso se transformar no efeito Makefile
“O efeito Makefile se parece com outros fenômenos, como culto à carga, normalização do desvio e ‘linguagens somente de escrita’. Neste texto, vou argumentar que ele difere um pouco de cada um deles por tratar das consequências de um determinado design, e não de algo essencialmente ineficiente ou ruim”
Nesse caso, se alguém realmente quiser “escapar da ilha e pilotar o avião”, isso quer dizer que, no fim, a universidade é o caminho mais próximo do “real”?
Sei que é difícil falar em termos absolutos, que há muitas exceções e que cada pessoa tem várias justificativas do tipo “eu não consigo fazer isso”. Aqui estou perguntando do ponto de vista de uma teoria ótima. O caminho ótimo para evitar comportamento de culto é entender o todo, e esse “todo” não vem do ensino superior?
Logicamente, mesmo tentando estudar livros com seriedade para alcançar ou superar a “completude” da universidade, parece que em alguns aspectos isso ainda ficaria aquém do tempo passado em sala de aula com educadores conhecedores. Mesmo supondo que a mesma pessoa se esforce igualmente nos dois caminhos
Outro fator é a frequência de uso. Escrevo textos grandes em LaTeX no máximo uma vez por ano
No nível em que uso LaTeX, ele não é uma ferramenta difícil, mas, se você só o usa por algumas semanas por ano, nunca consegue lembrar os detalhes de uso; por isso, geralmente começo um novo documento copiando e colando um documento anterior
Mas, mesmo tendo usado LaTeX muito no passado, sempre copiei e colei a partir de templates. Apresentações em beamer e figuras em TikZ eram ainda piores: eu copiava de apresentações ou figuras anteriores, mais do que de templates
Acho que o TikZ é uma ferramenta intrinsecamente complexa e que eu não a aprendi o suficiente
Já o LaTeX eu acho que aprendi o bastante, então pode haver outro motivo
Na minha opinião, pode ser uma questão de padrões normais. Uma boa ferramenta deveria oferecer bons padrões. Mas o LaTeX não é uma boa ferramenta nesse critério. Meus documentos em geral começam mais ou menos assim A maior parte disso serve para obter suporte básico a não ASCII necessário para minha língua materna, ou para ativar padrões normais como papel A4 e microtype. Com ferramentas modernas como pandoc/Markdown, talvez isso não fosse necessário
Então, muitas vezes, o objetivo de copiar e colar essas coisas é obter bons padrões que uma ferramenta melhor já teria fornecido desde o início
A menos que você não use matemática ou escreva apenas texto puro em inglês, o LaTeX é quase “sem baterias inclusas”
Isto é desenvolvimento movido a copiar e colar [0], e não é uma questão relacionada apenas a Makefiles. Tem a ver com o setor inteiro copiando código de um lado para outro sem nem saber o que está copiando
Sinceramente, acho que o Copilot piorou isso, porque as pessoas estão aceitando blocos de código às cegas na base de código
[0] https://andrew.grahamyooll.com/blog/copy-pasta-driven-develo...
makeprovavelmente pelo menos 7 vezes seguidas, mas nunca tive tempo para lidar com isso todos os dias até memorizar completamenteEm algum momento, simplesmente desisti. Virou um conhecimento esotérico obscuro para o qual não consigo criar memória muscular e que preciso reaprender do zero sempre que preciso. Então migrei para ferramentas mais simples
A perda do trabalho profundo não é culpa de bons programadores, mas do pessoal de negócios
Já vi muitas vezes alguém copiar um Makefile de outro projeto e depois modificá-lo para “funcionar”, deixando intactas etapas de build e targets desnecessários que não tinham nenhuma relação
Pessoalmente, isso me incomoda muito
Por tentativa e erro? Então divirta-se :p
É verdade que há muitas ferramentas mais complexas do que o necessário, mas não acho que o Make em si seja um bom exemplo disso
Em ferramentas modernas, o problema da complexidade muitas vezes é agravado por documentação insuficiente, documentação em estilo livro de receitas, documentação que não explica o modelo conceitual necessário para inferir como a ferramenta funciona e documentação que não tem uma especificação suficientemente detalhada e precisa
O Make não é assim. Ele é bem documentado e, se você de fato dedicar tempo para ler a documentação, não é difícil de entender
A abordagem em estilo livro de receitas acima leva a uma cultura que dá pouco valor a aprender e entender as ferramentas que se usa, e à importância da documentação minuciosa que torna isso possível. Ou talvez a causalidade seja o inverso
De qualquer forma, acho que o problema está em dedicar pouco tempo demais a criar documentação abrangente e atualizada para as ferramentas, em projetar pouco as ferramentas para que esse tipo de documentação seja possível desde o início, e em alocar recursos de menos para ensinar e aprender as ferramentas necessárias