Aquilo que acabou sustentando carga por acidente
(jefftk.com)- Estruturas ou código que originalmente pareciam triviais ou temporários podem, com o tempo, passar a ter a função de sustentar o sistema, portanto é preciso verificar as dependências atuais antes de fazer mudanças
- A Chesterton's Fence é um princípio útil: entenda por que algo foi instalado antes de removê-lo. Mas, ao olhar apenas para a intenção inicial, é possível deixar passar funções que surgiram depois
- Durante a reforma do banheiro de uma casa, uma viga vertical que parecia um obstáculo continuava sustentando parte da carga do segundo andar após a função de divisória de armário ter desaparecido, por causa de uma alteração estrutural incorreta
- Em sistemas computacionais complexos, o histórico de mudanças e a documentação de projeto são apenas o ponto de partida; também é preciso observar como o componente está atualmente integrado ao sistema
- Ao remover ou modificar componentes antigos, se você não verificar ao mesmo tempo o motivo de projeto do passado e a função oculta atual, pode acabar causando falhas inesperadas
O que a Chesterton's Fence pode deixar passar
- A Chesterton's Fence é a ideia de que, antes de alterar ou remover algo, é preciso primeiro entender por que aquilo foi criado
- Algo feito por pessoas, como uma cerca ou um portão, geralmente tem grande chance de ter existido por um motivo que alguém julgou útil
- Mesmo um projeto que pareça totalmente sem sentido pode indicar que a pessoa que quer alterá-lo deixou passar algum aspecto do problema
- No entanto, essa perspectiva pode fazer com que se foque apenas na função pretendida pela pessoa que o criou originalmente, ignorando novas dependências que surgiram depois
Sustentação de carga acidental descoberta em uma reforma
- Alguns anos atrás, ao refazer o banheiro de uma casa, havia uma viga vertical que atrapalhava o trabalho
- Ela originalmente fazia parte de uma divisória de armário, e a função dessa divisória parecia não ser mais necessária
- Olhando apenas pela perspectiva da Chesterton's Fence, parecia seguro removê-la, mas, na prática, com o tempo, ela havia se tornado uma estrutura de sustentação de carga
- Após outras alterações estruturais incorretas, essa viga passou a contribuir para sustentar o segundo andar da casa
- É preciso verificar não só por que algo foi criado no início, mas também quais funções adicionais ele passou a desempenhar depois
Lições para sistemas computacionais complexos
- O mesmo problema se repete ao alterar sistemas computacionais complexos
- Examinar o histórico de mudanças, ler a documentação original de projeto e entender por que determinado componente foi feito daquele jeito continuam sendo práticas úteis
- Porém, para fazer uma mudança segura, também é necessário analisar de que maneira esse componente está atualmente conectado e sendo usado dentro do sistema
- Com o tempo, componentes tendem a assumir funções secundárias diferentes do propósito original
- Uma mudança segura começa por verificar, ao mesmo tempo, a intenção de projeto do passado e a função real exercida no presente
1 comentários
Opiniões do Hacker News
Finalmente parece que temos um nome para isso
Trabalho bastante com suporte a sistemas de controle, e não é raro código de PLC que lida de forma peculiar com algum equipamento físico acabar criando problemas não intencionais
Costumo repetir a frase: “a cada vez que você corrige um problema elétrico/mecânico com software, nasce um gremlin”
Mesmo quando descubro a causa raiz de um bug ou uma limitação programada que eu gostaria de remover, sempre recuso até entender por que aquele código existe. Nenhum código é colocado ali sem motivo, então primeiro é preciso descobrir por que aquele timer ou override é necessário
Às vezes é bom quando o código resolvia um problema que já não existe, mas também é comum ele ter sido colocado para evitar algum acidente e, com a troca de pessoal, o propósito original ter se perdido. Sem documentação, fico muito cauteloso para desfazer imediatamente o trabalho de outra pessoa
Também há a questão de confiar nos colegas. Normalmente ninguém faz algo sem motivo, então, se o código está lá, devo assumir que há um propósito e acreditar que ele foi suficientemente avaliado no início. Quando essa confiança se rompe, toda tomada de decisão fica difícil
Se o motivo envolve interagir com algo fora da base de código, como sistema operacional, sistema de arquivos, banco de dados, endpoint HTTP ou hardware, eu comento em 100% dos casos, a menos que seja uma simples chamada de API ou biblioteca
Se coloquei um
sleeppor causa do limite de taxa de outro serviço, escrevo quem exige isso, os valores de limite conhecidos na época, se não souber exatamente, que é “uma estimativa, mas parece funcionar”, e como o sistema pode se comportar quando o limite é excedidoSe uso um banco de dados para uma tarefa pequena que aparentemente poderia ser feita com o sistema de arquivos, mas na prática acessar o sistema de arquivos da forma necessária naquele ambiente esgota recursos por causa de chamadas de sistema que disparam sob carga, deixo um comentário
Workarounds para bugs de bibliotecas amplamente usadas que o Ubuntu não corrige em versões LTS também merecem comentários. Já escrevi muitos comentários do tipo “sei que isto não é ideal, mas o motivo é este”
Também comento quando estou com preguiça de fazer algo escalável agora e, pelas previsões atuais, isso nem será necessário, mas o código não funcionaria bem em grande escala. Talvez seja mais para proteger o orgulho, mas é algo como: “sei que isto lê o arquivo inteiro na memória, mas é um job em lote raro e previsível, e os arquivos devem ser pequenos, então está tudo bem. Se faltar memória, olhe aqui primeiro. Se for transformar em chamada sob demanda, reescreva”
O que o texto quer apontar é que isso por si só não basta. Porque também é preciso saber o que mais foi construído sobre a premissa de que aquele código existe
“Não sei por que este rung é necessário, mas apague e descubra por conta própria”
Não mexi nele sem motivo, nem tentei descobrir
Engenheiros elétricos e mecânicos historicamente levaram software menos a sério do que sistemas elétricos e mecânicos, e como resultado é fácil aparecer código bagunçado em culturas de engenharia dominadas por EE/ME
Mesmo entre pessoas que, na superfície, são Professional Engineers, ainda é comum encontrar engenharia de software imatura a um ponto difícil de aceitar
Código sem documentação já é uma coisa; pior é que, em geral, é hardware customizado de 30 anos atrás, e nem sequer há esquemáticos do equipamento em que você está trabalhando
Passei por algo parecido
Alguns anos atrás comprei uma casa antiga, e os donos anteriores vinham fazendo a maior parte das obras por conta própria desde os anos 1960
A calha de zinco provavelmente ficou vazando por décadas e danificou parte da estrutura do telhado, e o telhado estava sendo sustentado por painéis de madeira instalados nos anos 70 para cobrir a parte interna. Ou seja, aqueles painéis de madeira estavam de fato suportando carga
Descobri muito mais coisas nessa casa. Por exemplo, quando faltou largura de telha na beirada do telhado, em vez de comprar telhas extras, preencheram com cimento e pedaços de vasos de cerâmica quebrados
Hoje, contamos com drywall e compensado para cumprir esse papel em terremotos e tempestades
Se você vir uma casa sendo reconstruída até ficar só no esqueleto, verá que alguns reforços são acrescentados. Não é para impedir que a parede caia, mas para manter ângulos retos e planos até que a parede seja reerguida
À primeira vista parece que alguém bateu na porta da garagem e a amassou feio, mas olhando de perto o telhado está se segurando por um fio nos trilhos onde a porta fica presa e está quase no fim
No começo eu só queria remendar as pontas dos caibros e trocar a porta da garagem, na lógica de que alguém já tinha feito isso do outro lado antes e, se funcionou, então servia; agora acho que vou ter que refazer o telhado inteiro
O que realmente me preocupa é a fiação suspeita espalhada pelo porão inteiro. Há uma mistura de fios relativamente novos, fios antigos com isolamento de tecido e fita isolante juntando tudo isso. Felizmente, nenhum dos fios parece estar suportando carga
Este texto e quase todos os comentários parecem estar deixando passar o problema real. O ponto central é falta de testes
Diferentemente de todos os outros meios de produção, no software é possível de fato testar mudanças antes de refletí-las na realidade
Se houver bons testes, não importa qual era a intenção, nem se a funcionalidade ganhou um novo uso ou novos usuários. Você corrige, roda os testes, e eles dizem se a correção é boa
Com bons testes, não é preciso arqueologia de software, veteranos experientes que conhecem todas as rachaduras, prodígios que modelam sistemas complexos na cabeça, documentos abrangentes de requisitos, nem sistemas cautelosos de implantação que fazem de alguns grupos de usuários cobaias
Com bons testes, dá até para alterar o sistema aleatoriamente e parar quando surgir uma melhoria. Exatamente como o Google relatou que a IA “desenvolveu” uma melhoria de alinhamento
Ainda assim, desenvolvedores de testes recebem menos da metade, departamentos de testes são relativamente pequenos, QA fica espremido em cronogramas fixos e limitados, e quase não há heróis técnicos vindos de QA. Talvez porque isso pareça um trabalho derivado e reativo
Você pode remover código não usado e seus testes, mas ele na verdade ainda estar em uso
Depois da mudança, um teste pode falhar; aí você ajusta o teste para a nova situação por ele ser frágil, mas depois descobre que algo dependia do comportamento antigo
Testes são ótimos e, em sistemas suficientemente autocontidos, podem bastar. Mas, em sistemas maiores, às vezes também é preciso telemetria ou implantação gradual
O código original servia para calcular o VAT de uma lista de compras, mas aos poucos pode ter virado um meio de forçar a atualização do cache de VAT por categoria de produto e ser chamado em contextos que não foram previstos inicialmente
Comentários são a mesma coisa. Eles tratam da intenção original e dos efeitos colaterais, mas não cobrem onde aquele método ou classe passou a ser usado muito depois, nem o que ele acabou realmente fazendo
Em um mundo ideal, comentários também seriam atualizados quando o mundo ao redor muda, mas, na prática, isso quase nunca acontece a menos que o código interno mude junto
Em geral, os testes apodrecem. Parece que testes também têm prazo de validade e, no fim, alguns começam a morrer
Problemas de dependências, mudanças nas expectativas de APIs, atualizações de segurança, expiração de contas e credenciais, mudanças em endpoints e estados de máquinas se misturam até que o resultado dos testes deixe de indicar a correção do programa
O valor comercial marginal de consertar um único teste quebrado costuma ser muito baixo, então muitas vezes ele é simplesmente desligado ou forçado a “passar” mesmo quando deveria ser erro
Depois de 10, 20 anos repetindo isso, logo surge uma divisão entre “testes nos quais realmente confiamos” e “testes que estamos ocupados demais para corrigir ou limpar”
Quais testes são bons ou ruins vira conhecimento tribal que desaparece com mudanças de cargo e função, e em algum momento o próprio monte de “testes que mentem dizendo que funcionam” e “testes cujas falhas ninguém mais investiga se são verdadeiras” acaba, por acaso, sustentando carga
Seria melhor dizer que programadores devem escrever testes, mantê-los junto com o código e executá-los automaticamente no processo de build
Ainda assim, acho que nem um desenvolvimento orientado a testes bem-feito substitui um bom design e boas práticas. Nem mesmo uma especificação muito simples pode ser substituída por testes
Se
f(S)estiver especificada apenas como retornando a string concatenada consigo mesma, é difícil verificar quefestá correta só com testes óbvios que tratemfcomo uma caixa-preta. Especificação formal também é importanteDá para testar alguns pontos, mas, se um único valor errado mágico for fatal, o teste não vai mostrar isso
Arqueologia de software, veteranos experientes, prodígios que modelam o sistema na cabeça, documentos abrangentes de requisitos e sistemas de implantação que usam alguns usuários como cobaias podem ser satirizados, mas todos são respostas ao fato de que software é difícil. E software é realmente difícil
Normalmente, equipes de software precisam ser diretamente responsáveis pela qualidade do próprio trabalho e não podem empurrar problemas para além do organograma
Dá para entender que uma viga vertical que não era importante acabe recebendo carga depois, mas, pela minha experiência, isso parece sinal de projeto preguiçoso
Pelo menos ao criar software, dá para perceber quando se está tentando sustentar parte da casa com uma viga decorativa; e, se você escolhe simplesmente deixar assim em vez de criar uma nova estrutura melhor, a equipe de desenvolvimento acaba ficando bem deprimida depois
Concordo com o texto, mas é muito melhor trabalhar em um lugar onde você possa esperar não fazer esse tipo de descoberta com frequência
O ponto do texto não é tanto dizer para não usar uma viga decorativa como elemento de sustentação, e sim reconhecer que alguém pode ter feito isso antes de você chegar
Essa é uma posição ainda mais conservadora do que a interpretação básica da cerca de Chesterton, e até essa interpretação básica muita gente rejeita por considerá-la restritiva demais
Para mim, o texto faz sentido. Em termos de programação, já vivi de fato a experiência de remover uma moldura “decorativa” e o teto desabar sobre a minha cabeça
Se um sistema é robusto ou perigosamente impossível de escalar depende do contexto
Sempre dá para fazer experimentos mentais como “e se a equipe de vendas dobrar de tamanho e vender e integrar clientes o mais rápido possível até capturar 100% do mercado?”, e talvez, mesmo nessas condições, usar o banco de dados como uma fila de mensagens seja aceitável
Se o resultado foi uma equipe de desenvolvimento sofrendo, então provavelmente foi um erro. Ficou difícil de manter ou virou um inferno operacional
Mas usar uma viga decorativa de software como elemento de sustentação não leva necessariamente a esse resultado. Muitos sistemas economizam meses que seriam gastos criando a solução correta e cumprem seu papel de forma invisível e feliz
O restante da base de código nunca envia entrada inválida, então esse ramo é código morto e não recebe carga
Aí, em algum momento, entra um bug que envia entrada inválida, e esse ramo a trata e se recupera diligentemente. Nesse instante, ele se torna um ramo de sustentação
Meu artefato favorito entre os que vi “receberem carga por acaso” foi um
sudomal configuradoEle permitia
sudosem senha para o comandofind, o que tornava fácil executar código arbitrário como root com-exec, e vários scripts de suporte importantes do produto tinham sido escritos para usar issoEra uma elevação de privilégio de sustentação, por assim dizer
Alguns anos atrás, reformei a cozinha
Em uma extremidade da cozinha antiga havia uma viga grande, que tinha sido acrescentada em alguma reforma antes de comprarmos a casa para sustentar o segundo andar. Para ampliar a cozinha, precisávamos removê-la
Ao abrir o teto, descobrimos que a viga estava cerca de 60 cm à direita de onde deveria estar para sustentar a parede do andar de cima
No fim, consertamos e deslocamos a viga para dentro da parede do andar superior, e deu tudo certo; mas, quando perguntei sobre a posição original, o empreiteiro disse algo mais ou menos assim:
“Havia uma pessoa tentando fazer direito e outra que não se importava. No fim, a qualidade se ajusta ao menor valor de configuração”
Uma vantagem do software sobre sistemas físicos é que dá para documentar facilmente a intenção dentro do código com comentários e tipos, tornando-a mais explícita
Especialmente em linguagens dinâmicas como Python, isso não é perfeito, mas ajuda muito
A analogia com uma viga de sustentação pode ser um projeto de hackathon que ninguém imaginava que iria para produção
Na prática, muito do que fazemos é improvisar algo até que mal funcione e então passar para a próxima coisa
O plano de manutenção precisa saber que “cargas” cada peça ou conjunto substituível suporta
Infelizmente, a engenharia de sistemas de hoje se afastou muito de seus objetivos originais, mas a ideia inicial era essa
Um dos motivos pelos quais os departamentos de engenharia de sistemas hoje têm relativamente menos poder é que finanças entrou no planejamento de manutenção. A depreciação de estoque é brutal, e “o que manter como peça sobressalente”, pelo menos na minha experiência, raramente é mais uma decisão da engenharia de sistemas
O resultado é previsível, embora seja parcialmente compensado pelo padrão muito alto do pessoal de manutenção aeroespacial. Eles são bastante excelentes quando comparados, por exemplo, a técnicos de conserto de máquinas de lavar
Claro que finanças também gostaria de baixar esse padrão em vários níveis
Mas sempre me surpreende a frequência com que vejo sistemas em que um componente a montante que parece “decorativo” na verdade impõe uma limitação de velocidade, e, se ele é removido, o restante sai disparado sem controle
Isso me faz pensar nos casos em que usuários aproveitam sem perceber um bug do software e o incorporam ao fluxo normal de trabalho
Como resultado, ao corrigir o bug, o fluxo de trabalho quebra e surgem reclamações
O texto diz que “era fácil saber por que ela estava ali. Fazia parte da divisória do armário”, mas também que, com o tempo, ela acabou recebendo carga por acaso e, por meio de outras alterações estruturais incorretas, essa viga agora ajudava a sustentar o segundo andar da casa
Mas claramente não era fácil saber por que ela estava ali. Além disso, também não estou convencido de que ela tenha acabado recebendo carga por acaso
Parece bem possível que alguém a tenha feito receber carga intencionalmente, por motivos que, embora pareçam errados para você, não pareciam errados para as pessoas da época
Só que saber por que ela estava ali no início não diz o que ela está fazendo agora
Um pós-doutor em física com quem trabalhei costumava colocar, às vezes, este aviso sobre configurações de equipamentos
“Não mexa. Há perigos ocultos”
O laboratório era cheio de gente inteligente, acostumada a olhar para algo e chegar sozinha a uma conclusão racional sobre se podia mudar aquilo
O aviso era uma advertência para não fazer esse julgamento rápido demais