3 pontos por GN⁺ 2023-07-21 | 1 comentários | Compartilhar no WhatsApp
  • 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

 
GN⁺ 2023-07-21
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

    • Por isso, em linhas de código que não sejam extremamente óbvias, sempre deixo um comentário explicando por quê
      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 sleep por 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 é excedido
      Se 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”
    • Acho que isso é basicamente a lógica comum da cerca de Chesterton
      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
    • O comentário mais assustador que já vi nas profundezas de um PLC Allen Bradley foi este:
      “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
    • Parte disso também é cultural
      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
    • Acho que nunca mais vou trabalhar com PLC
      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

    • Voltando ao começo dos anos 1900, o revestimento externo era instalado na diagonal e ajudava bastante a impedir que a construção torcesse
      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
    • Minha garagem é exatamente essa sensação
      À 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
    • Faltam poucos passos até termos tinta suportando carga
    • Havia uma casa com danos muito graves de cupins, e o empreiteiro chamou aquilo de estuque estrutural
  • 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

    • Mesmo que os testes verifiquem o comportamento para o qual o código foi projetado, pode haver outros sistemas que passaram a depender do que o código realmente faz
      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
    • Testes têm um escopo específico. É o escopo pelo qual o código deve ser responsável, não necessariamente tudo que, ao ser usado por meses ou anos, passou a ser esperado dele
      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
    • Se você trabalha em projetos de vida muito longa, testes ou um QA com orçamento suficiente não impedem problemas organizacionais
      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
    • O começo parecia repetir o otimismo do desenvolvimento orientado a testes, mas de repente passa a falar de departamento de testes, o que deixa o argumento menos consistente
      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 que f está correta só com testes óbvios que tratem f como uma caixa-preta. Especificação formal também é importante
      Dá 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
    • A própria distinção entre desenvolvedores de testes, departamentos de testes e equipes de QA é quase um luxo para a maioria das organizações de software
      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

    • No que “você” construiu, “você” talvez saiba, mas na minha carreira lidei e refiz muito mais coisas criadas por outras pessoas
      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
    • Será que é sempre preguiça no mau sentido? Em software, não existe uma divisão nítida entre “feito para receber carga” e “feito para prender o drywall”
      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
    • Digamos que você programe de forma defensiva. Adiciona a uma função um tratamento para entrada inválida
      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
    • Já aconteceu de eu achar que estava mantendo bem um serviço sem importância e, quando ele caiu, descobrir que outra equipe tinha começado a depender dele para uma função essencial do negócio, numa situação em que originalmente nada deveria ter acontecido
    • O que vi com mais frequência foi que isso acontece justamente porque, na prática, ninguém sabe disso
  • Meu artefato favorito entre os que vi “receberem carga por acaso” foi um sudo mal configurado
    Ele permitia sudo sem senha para o comando find, 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 isso
    Era 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

    • Exato. Foi por causa exatamente desses problemas que a engenharia de sistemas foi introduzida nos setores aeroespacial e de defesa
      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
    • Quando é intencional, é fácil fazer isso
      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

    • Dava para saber por que ela estava ali
      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