2 pontos por GN⁺ 2023-10-31 | 1 comentários | Compartilhar no WhatsApp
  • A maior lição recorrente de muitos anos de experiência em desenvolvimento é que a complexidade é a “inimiga eterna” de uma base de código, criando um estado em que uma pequena mudança quebra algo em um lugar inesperado
  • A ferramenta mais forte para reduzir a complexidade é dizer “no” a funcionalidades e abstrações, mas quando é preciso fazer concessões realistas, uma solução 80/20 entrega a maior parte do valor com pouco código
  • Design e testes devem dar mais peso a encontrar bons pontos de fronteira depois que a forma do sistema se revela, em vez de abstrações cedo demais; e valorizam testes de integração, que duram mais, em vez de testes unitários
  • Ferramentas, sistemas de tipos, logging, debuggers e design de APIs devem reduzir a carga cognitiva do desenvolvedor; o grande valor prático dos sistemas de tipos está no autocomplete da IDE
  • Técnicas com bons nomes, como DRY, separação de interesses, microsserviços, SPA, genéricos e refatoração excessiva, também viram caminhos para a complexidade quando dificultam o entendimento e a manutenção do código

Complexidade é o principal risco de uma base de código

  • A complexidade faz um sistema que inicialmente era compreensível se tornar de repente difícil de lidar, e uma mudança em um ponto quebra outro que parecia não ter relação
  • A complexidade pode entrar por meio de desenvolvedores ou gerentes de projeto bem-intencionados e, às vezes, a complexidade que você mesmo introduziu é a causa do problema
  • Não existe uma “clava” universal para resolver a complexidade; a melhor resposta é não deixá-la entrar desde o começo

“no” e “ok”: recusa e concessão realista

  • A melhor arma para impedir a complexidade é “no
    • “Não vamos criar essa funcionalidade”
    • “Não vamos criar essa abstração”
  • Porém, embora “no” seja um bom conselho de engenharia, pode ser desvantajoso para ganhar mais recompensas ou promoções
  • Quando for realisticamente necessário fazer concessões, responda “ok” e procure uma solução 80/20
    • Uma forma de implementar 80% do valor desejado com 20% do código
    • Pode não ter todos os enfeites e ficar um pouco feia, mas entrega a maior parte do valor mantendo a complexidade baixa
  • Como gerentes de projeto também podem esquecer requisitos detalhados, mudar de área ou sair da empresa, às vezes a abordagem 80/20 acaba sendo a melhor escolha

Design e decomposição do código são perigosos cedo demais

  • No início de um projeto, a forma do sistema ainda é indistinta, como “água”, então evite decomposição cedo demais
  • Com o tempo, bons pontos de fronteira aparecem
    • A interface com o restante do sistema é estreita
    • Um pequeno número de funções ou abstrações esconde a complexidade interna
  • Criar abstrações cedo demais tende a gerar abstrações erradas, então é melhor pesar mais para o lado de esperar
  • Para desenvolvedores que querem criar grandes abstrações no começo, pode ajudar pedir artefatos descartáveis, como UML, ou exigir uma demo funcionando amanhã
  • Um protótipo inicial mostra mais rapidamente código que realmente funciona e restrições realistas

Testes não são “deixar tudo para depois”, mas “acompanhar a forma que surge”

  • Testes evitam muitos problemas, mas o texto se opõe a impor test first antes mesmo de entender o domínio
  • A preferência é escrever a maior parte dos testes quando, depois do protótipo, o código começa a se firmar
  • A atitude de pular testes dizendo “na minha máquina funciona” é muito ruim
  • As preferências por tipo de teste são claras
    • testes unitários: úteis no início do projeto, mas quebram facilmente com mudanças de implementação e podem dificultar refatorações
    • end to end: mostram o comportamento do sistema inteiro, mas quando quebram é difícil entender a causa e, se quebram com frequência, tendem a ser ignorados
    • testes de integração: altos o suficiente para observar a correção do sistema e baixos o suficiente para encontrar a causa com um debugger, chegando perto do melhor ponto
  • Quando a API de um ponto de fronteira se estabiliza, invista fortemente em testes de integração ao redor dela
  • Mantenha os testes end-to-end como um conjunto pequeno e bem gerenciado, concentrado nas funções de UI mais comuns e em alguns edge cases importantes
  • Use mocking só quando for realmente necessário e, se possível, apenas no nível de grandes pontos de fronteira
  • Quando um bug é encontrado, a preferência é, excepcionalmente, reproduzi-lo primeiro com um teste de regressão e depois corrigi-lo

Agile, refatoração e Chesterton's Fence

  • Agile em si não é o pior e pode ser usado como uma das formas de organizar uma equipe de desenvolvimento, mas é preciso ter cuidado com o “agile shaman” que usa Agile como uma espécie de isenção de culpa pelo fracasso
  • Protótipos, boas ferramentas e a contratação de bons desenvolvedores são mais importantes para o sucesso de software; nenhum processo é uma silver bullet que resolve todos os problemas
  • Refatoração é especialmente útil quando o código já se firmou na fase final do projeto, mas quanto maior a refatoração, maior a chance de fracasso
    • Faça em partes pequenas
    • Mantenha o sistema funcionando tanto quanto possível
    • Passe para a próxima etapa depois de concluir cada uma
  • Abstração excessiva pode levar ao fracasso da refatoração e do sistema
    • J2EE é um exemplo que prejudicou muitos projetos
    • OSGi tentou conter a complexidade, mas a tornou mais forte, exigindo vários man-years de retrabalho
  • Chesterton's Fence se conecta à postura de não remover nem mesmo código feio antes de entender por que ele existe
  • Código em funcionamento deve ser respeitado, mesmo que não seja perfeito; quanto maior o sistema, mais tempo é necessário para entendê-lo primeiro

Microsserviços e ferramentas

  • Microsserviços acrescentam chamadas de rede ao problema já difícil de dividir corretamente um sistema
  • Ferramentas aumentam muito a produtividade e a compreensão dos desenvolvedores
    • Em um novo ambiente, é melhor gastar tempo aprendendo as ferramentas
    • Duas semanas aprendendo ferramentas podem dobrar a velocidade de desenvolvimento
    • Se não houver documentação, é preciso perguntar a outros desenvolvedores e investigar
  • O code completion da IDE evita que seja preciso memorizar todas as APIs e é quase indispensável no desenvolvimento Java
  • Um bom debugger é muito importante
    • Breakpoints condicionais
    • Avaliação de expressões
    • Navegação pela stack
  • Novos desenvolvedores devem aprender profundamente o debugger disponível, e isso pode ensinar mais sobre computadores do que aulas universitárias

Sistema de tipos, expressões e DRY

  • O maior valor de um sistema de tipos está no suporte das ferramentas, que mostra uma lista de ações possíveis quando se digita um ponto
  • Correção de tipos também é boa, mas autocomplete e navegação no código têm um valor prático maior
  • Desenvolvedores que usam o sistema de tipos de forma abstrata demais merecem cautela
    • Genéricos podem ser especialmente perigosos
    • Na maioria dos casos, a preferência é limitá-los a algo como classes de contêiner
  • Em vez de comprimir expressões condicionais curtas em uma linha, é melhor dividi-las em variáveis intermediárias com nomes significativos, o que ajuda no debugging e na compreensão
  • DRY é um bom conselho, mas exige equilíbrio
    • Código duplicado simples e claro às vezes é melhor do que callbacks, closures e modelos de objeto complexos
    • Se a remoção da duplicação em si aumenta a complexidade, pode ser um prejuízo

Separação de interesses, closures e logging

  • Separação de interesses é uma ideia poderosa, mas na prática pode fazer você perder tempo pulando entre vários arquivos para entender o comportamento
  • Como alternativa, a preferência é por locality of behavior
    • Se o código fica perto “da coisa que funciona”, ao olhar para esse alvo você sabe imediatamente o que ele faz
  • Closures são adequadas para abstrair operações em coleções, mas uma pequena quantidade, como sal, é suficiente
  • O “callback hell” em JavaScript é um exemplo de complexidade causada pelo uso excessivo de closures
  • Logging é muito importante, especialmente em ambientes de deploy na nuvem
    • Registre logs em cada ramificação lógica principal
    • Em requisições que passam por várias máquinas, inclua um request ID para conseguir agrupar os logs
    • Se possível, ajuste dinamicamente o nível de log
    • Se possível, ajuste o nível de log por usuário para depurar problemas de um usuário específico
  • Bibliotecas de logging podem ser complexas, mas investir em uma boa infraestrutura de logging traz grandes recompensas depois

Concorrência, otimização e API

  • Concorrência é uma área digna de receio, e a preferência é por modelos tão simples quanto possível
    • Handlers de requisições web sem estado
    • Filas de tarefas remotas que não dependem umas das outras
    • APIs simples
  • Na web, concorrência otimista pode se encaixar bem
  • Estruturas de dados concorrentes como ConcurrentHashMap, do Java, também devem ser usadas com cuidado
  • Otimização deve começar depois que um perfil real de desempenho identificar um problema concreto
    • Não olhe apenas para CPU
    • Acesso à rede equivale a muitos ciclos de CPU, então deve ser reduzido quando possível
    • Ver loops aninhados e partir imediatamente para remover O(n^2) pode aumentar a complexidade
  • Uma boa API deve evitar que o desenvolvedor precise pensar muito
    • Se o autor da API fica preso à perspectiva da implementação ou do domínio, ela se torna difícil de usar
    • É bom ter camadas: oferecer uma API simples para casos simples e uma API mais complexa separada para casos complexos
    • Se for orientação a objetos, o comportamento deve ficar, tanto quanto possível, sobre o próprio objeto em questão
  • O exemplo de stream/collector do Java é criticado como uma API que obriga a contornar desnecessariamente uma tarefa comum

Parsing, Visitor Pattern e front-end

  • Parsers no estilo recursive descent são vistos como divertidos e bonitos
  • Parser generators tornam o código gerado difícil de entender e depurar, além de esconder a natureza recursiva da gramática
  • A maioria dos parsers reais em produção é recursive descent, e Bob Nystrom, com Crafting Interpreters, é recomendado
  • O Visitor pattern é resumido brevemente como “bad”
  • Separar front-end e back-end e conectar bibliotecas de SPA a uma API JSON GraphQL via HTTP é quase como criar dois ninhos de complexidade
  • Há cautela com a tendência de usar grandes bibliotecas de front-end até para sites simples ou tarefas de inserir formulários no banco de dados
  • Para reduzir a complexidade, foram criados htmx e hyperscript
    • Manter HTML simples
    • Evitar muito JavaScript
  • React é melhor para empregos e para alguns tipos de aplicação, mas é visto como uma escolha que entra no caminho da complexidade de front-end

Modas, FOLD e impostor syndrome

  • Desenvolvimento tem muitas modas, especialmente no front-end
  • É preciso ter desconfiança de novas abordagens ditas revolucionárias
    • Na computação, muitas ideias já foram tentadas ao menos uma vez
    • Ideias ruins podem voltar com nomes novos
  • É bom quando um desenvolvedor senior diz publicamente: “isso é complexo demais e difícil de entender”
  • FOLD é a sigla de Fear Of Looking Dumb, o estado de não conseguir admitir a complexidade por medo de parecer burro
    • Quando um senior diz que algo é complexo, juniors também têm mais facilidade para admitir que não entendem
    • FOLD é uma das principais fontes de força da complexidade, especialmente entre desenvolvedores jovens
  • Desenvolvedores alternam entre a sensação de dominar tudo e a de não saber nada, e impostor syndrome é comum
  • Se todos se sentem impostores, ninguém é impostor; desenvolvedores jovens podem seguir a carreira mesmo com frustração e preocupação

Leituras recomendadas e conclusão

1 comentários

 
GN⁺ 2023-10-31
Opiniões no Hacker News
  • É estranho como pessoas inteligentes são atraídas pela complexidade como mariposas pela chama
    Leva anos para aprender a lutar contra o impulso de fazer overengineering
    Quando você começa a enxergar isso, fica difícil ignorar, e hoje consigo perceber imediatamente se um código sofreu overengineering
    Infelizmente, cerca de 99% do código parece ter sofrido overengineering, e há um forte incentivo para que desenvolvedores maximizem sua própria insubstituibilidade e horas faturáveis
    Mesmo desenvolvedores que parecem totalmente mecânicos e sem ego cometem overengineering com frequência, e isso opera de forma inconsciente
    Muitos desenvolvedores não pensam conscientemente em cada linha de código que escrevem; apenas definem um objetivo e produzem o primeiro código que os aproxima um pouco desse objetivo, sem perceber que havia uma alternativa melhor a cada etapa

    • Na minha experiência, quase nunca vi o incentivo para aumentar a insubstituibilidade do desenvolvedor e as horas faturáveis ser a causa do overengineering
    • Continuo lidando com esse problema
      Primeiro, gosto de quebra-cabeças interessantes. Código comercial, especialmente quando é bem feito, costuma ser bem entediante, então inconscientemente acabo empurrando funcionalidades cuja implementação é divertida. E, nesse caso, “divertida” acaba significando uma funcionalidade que torna tudo excessivamente complexo
      Segundo, enquanto estou programando algo, todas as escolhas que faço parecem óbvias e necessárias. Só depois, quando fico um tempo afastado do código e tento entendê-lo de novo com um olhar novo, é que vejo o tamanho da bagunça que fiz
      Para a maioria dos problemas, a solução mais evidente costuma ser bastante complexa, e saber onde encontrar uma solução simples exige sabedoria, habilidade e conhecimento de domínio. Soluções simples e limpas quase nunca são óbvias
      “Estamos implementando aos poucos uma fila de mensagens feita pela metade. Vamos simplesmente usar uma fila de mensagens padrão”, “Estamos criando um protocolo customizado e cheio de bugs de framing binário. Vamos simplesmente usar protobuf/msgpack”, “Não vamos criar um protocolo RPC customizado para buscar dados; vamos usar REST sobre HTTP. Assim podemos colocar nginx no meio para fazer cache das respostas do backend e também descartar o cache customizado”
    • Isso me lembra uma frase de uma carta de algum escritor, algo como “ficou uma carta longa porque não tive tempo de escrever uma curta”
      No fim, se houver tempo e se a pessoa for recompensada por isso, é possível escrever código perfeito, simples e sem repetição. Mas, na maioria dos casos, a recompensa vem por lançar alguma coisa e demonstrar competência técnica pela quantidade de trabalho, inteligência e capacidade de design investidas nesse lançamento
      A conclusão natural é que tudo vira um amontoado inchado, superprojetado e provisório, reescrito a cada 3 a 7 anos
      Não vi dados sobre isso, mas imagino que a “meia-vida de reescrita” tenha correlação com a rotatividade ou o tempo médio de permanência das pessoas. Mesmo que as pessoas se esforcem mais para evitar overengineering, há grande chance de que tudo seja reescrito de qualquer forma
      Como perfeccionista, isso é realmente irritante, mas às vezes criar algo complexo e com overengineering é mais bem recompensado do que pensar por mais tempo na melhor solução simples. Deve haver organizações melhores, mas em 9 anos trabalhando como engenheiro de software ainda não encontrei uma
      Projetos open source são o melhor contraexemplo que me vem à cabeça, mas mesmo boas bibliotecas costumam ser reescritas ou ganhar novas versões quando o mantenedor muda. Os incentivos financeiros são muito diferentes entre o open source e a miscelânea corporativa que muitos engenheiros de software em tempo integral no HN veem. É parecido com comparar um artigo acadêmico bem lapidado com um e-mail de trabalho escrito de qualquer jeito
    • No meu caso, o overengineering normalmente vem do oposto. Surge de pensar demais ao escrever código e de ter uma ideia preconcebida de como a base de código deveria ser
      Lembro que o Casey, do Handmade Hero, chamava seu estilo de programação de “baseado em compressão”. É escrever o código primeiro e, depois, separar gradualmente o que pertence junto
      Você abstrai quando há repetição, não por projetar conscientemente uma abstração. Tenho usado cada vez mais esse método
    • Se a conclusão de que “99% do código parece ter sofrido overengineering” produz respostas parecidas 99% das vezes, você não acha que, mesmo que o que tenha aprendido até agora seja válido, talvez haja um problema no modelo mental que você construiu?
      Isso por si só não significa necessariamente que esteja errado, mas, quando se trata de avaliar a qualidade do trabalho de outras pessoas, parece bem provável que exista um viés forte
  • Gosto muito deste site e ele sempre me faz rir. Minha parte favorita, em especial, é sobre microsserviços
    “grug se pergunta por que cérebro grande pega problema mais difícil, que é dividir sistema corretamente, e ainda coloca chamadas de rede no meio. para grug, parece muito confuso”

    • Gosto tanto deste site que leio com frequência. Acho que as pessoas que me conhecem já devem estar cansadas do manifesto de desenvolvimento de software chamado grug brain
      “complexidade é muito, muito ruim”
      “se tiver que escolher entre complexidade e luta 1 contra 1 com tiranossauro, grug escolhe tiranossauro. pelo menos grug consegue ver tiranossauro”
  • Tenho algumas ressalvas sobre a parte de tipos
    O grug diz que mais de 90% do valor de um sistema de tipos está em fazer aparecer magicamente uma lista do que você pode fazer quando aperta ponto no teclado, mas os juniores da nossa empresa frequentemente publicam em produção código que quebra por acesso a null, e o Sentry nos avisa disso
    Em períodos de desenvolvimento intenso, aparece mais ou menos um bug de acesso a null detectado por dia para cada desenvolvedor júnior
    Se usássemos um sistema de tipos decente com verificação estática, o IDE avisaria “isso pode ser null, tem certeza?”, o que ajudaria muito
    E autocomplete é possível mesmo sem tipos estáticos
    A parte que diz “os xamãs de sistemas de tipos de cérebro grande dizem que a correção de tipos é o cerne de um sistema de tipos, mas grug viu que esses xamãs não publicam código com frequência. Código não publicado pode, em certo sentido, estar correto, mas não é a correção de que grug fala” é grosseira e desnecessária
    Já publiquei código principalmente em C, PHP, Python, Haskell e Python tipado, e em linguagens tipadas a taxa de bugs que chegam à produção é muito menor. Esse é um dos motivos pelos quais gosto de tipos
    Além disso, refatorar fica muito mais fácil. Ao mudar algo usado amplamente, dá para verificar de forma confiável, em segundos, os pontos de chamada quebrados em toda a base de código, e isso ajuda muito a melhorar iterativamente bases de código que vão crescendo

    • Isso está errado? Mesmo pela experiência que você mencionou, parece que você não aceita sistemas de tipos a ponto de impor provas formais
      Em vez disso, você acaba aceitando um sistema de tipos de compromisso, que captura alguns problemas e, acima de tudo, oferece a mágica do autocomplete, sem ficar preso a todos os detalhes necessários para uma prova completa de correção
    • É só uma piada. Talvez eu provavelmente esteja do lado de quem está sendo zoado, mas acho que não é preciso levar tão a sério
      Ele parece considerar tipagem estática no nível de C ou Java aceitável ou boa, e coisas mais sofisticadas vistas em Rust ou Scala como perda de tempo. Acho que ele está totalmente errado, mas a vida é assim
    • O texto original não é a favor de linguagens tipadas? No fim parece ser a mesma posição, então fiquei confuso
    • Acho que “xamã”, aqui, não se refere a pessoas como você
      Entendi “xamã de sistemas de tipos de cérebro grande” como alguém que não necessariamente escreve código, mas, por exemplo, vende cursos sobre o tema e deixa as pessoas empolgadas
      No começo do texto ele também usa “xamã” ao falar de Agile, e há bastante gente cujo trabalho está mais perto de vender cursos do que de desenvolver software de fato
    • Já trabalhei com um “xamã de sistemas de tipos de cérebro grande”. Na verdade, este texto é uma análise bastante gentil e generosa
      Trabalhar com pessoas obcecadas por hierarquias de tipos é difícil de aguentar, e as justificativas geralmente ficam no mundo do “e se” e do “mas isso não é sound”
      Boa parte da perda de qualidade causada por sistemas de tipos vem de pessoas que acreditam que, se colocarem tipos suficientes, podem impedir bugs completamente — e isso não é verdade
  • Acho que, nos próximos meses, todo mundo vai embarcar no trem do HTMX, isso vai chacoalhar a cabeça de muitos desenvolvedores jovens, economizar muita energia no mundo todo e deixar muita gente feliz
    Depois, quando esses jovens curiosos forem clicando nos links do htmx.org e descobrirem o hyperscript, vai ser como o momento em Um Drink no Inferno em que os vampiros aparecem

    • Todo mundo embarcar no trem do HTMX é o loop infinito do JavaScript
      Alguém cansado da complexidade do JavaScript cria uma biblioteca JavaScript simples; as pessoas são atraídas por essa simplicidade; a biblioteca fica complexa porque precisa dar suporte a todos os recursos da Web; então alguém cansado dessa complexidade cria uma biblioteca JavaScript simples de novo
      Comecei com JavaScript puro, briguei por anos com IE5/6, o jQuery salvou todo mundo, pulei o AngularJS porque não era grande coisa, e gostei do beta do React quando saiu por causa dos ideais de programação funcional. Agora estou abrindo caminho por transpilation, hot reload, Typescript e uma pilha pesada de bibliotecas React
      Também criei alguns projetos com Intercooler e HTMX. HTMX é sólido no geral, mas não é o primeiro trem a passar por esta cidade
    • Ah, o Carson Gross era o autor de grug brained developer e do htmx
      Gostaria de ver críticas de verdade ao htmx
      Pessoalmente, acho que um grande desafio contínuo da Web tem sido encontrar uma boa forma de atualizar páginas, e nós continuamos tentando de tudo. Isso gera fadiga, e algumas ideias acabam ficando radicais ou virando uma bagunça com o tempo
      O Htmx parece uma reação no estilo grug: fazer menos, insistir em YAGNI
      O futuro é incerto, mas trabalhos como signals ainda parecem algo sagrado. Há muitas tentativas acontecendo em várias direções, e ainda estamos explorando esta frente e outras
      MobX, Svelte e outros também já percorreram um longo caminho. Quero acreditar que a jornada ainda não ter acabado e continuarmos enredados com o monstro da complexidade não é tanto um sinal de fraqueza — embora uma mentalidade conservadora ou grug possa ver assim —, mas sim um esforço necessário para chegar não a uma abordagem apenas simples, e sim a algo simples e bom
    • Ri ao ver aquela frase e depois fui conferir de verdade
      _="on load wait 5s then transition opacity to 0 then remove me"
      Ah, não
      _="on htmx:error(errorInfo) fetch /errors {method:'POST', body:{errorInfo:errorInfo} as JSON} "
      Ah, não!
  • Trabalho como desenvolvedor há 30 anos e admito que, no começo, eu era arrogante e achava que era mais inteligente que os outros. Naquela época eu era do lado do “cérebro grande”, que gostava de todos os demônios da complexidade
    Dez anos depois, fiquei mais próximo de um desenvolvedor “Grug brain”, e hoje me concentro na solução mais simples que funcione, mesmo sabendo que ela pode não ser perfeita. E tudo bem, porque isso nos aproxima do correto e permite melhorias iterativas
    A melhor coisa que você pode fazer como desenvolvedor é apagar código. Agora temos um requisito que mantivemos por 2 anos e que de repente deixou de ser requisito, e estou realmente animado para arrancar um monte de código, porque tudo vai ficar mais simples

    • Ao apagar código, há uma sensação agradável e difícil de descrever na parte de trás da cabeça
      É como se você sentisse fisicamente um espaço sendo liberado
  • Grug apareceu também nos comentários do texto Philosophy of Software Design, de uma semana antes, e acho que a discussão ali foi bem boa. https://news.ycombinator.com/item?id=38011938
    “Tenho exatamente a mesma sensação sobre grug. Não acho que as pessoas realmente concordem sobre o que é simples, então fingir que a sua ‘simplicidade’ é algo óbvio com que até um homem das cavernas concordaria soa como arrogância”
    Simplicidade muitas vezes é uma das coisas mais complexas de descobrir. Já vi muita gente convencida de que tudo ao seu redor — exceto o que ela própria faz — é complexo e precisa ser dobrado em direção à simplicidade. Parece uma postura perigosamente fraca, agarrada à autoridade

    • Bom ponto. Ainda assim, acho que o cerne é que, no fim, uma filosofia ruim incentiva a complexidade
      A filosofia grug brain é escolher a simplicidade a qualquer custo, exceto quando ela for absolutamente inevitável
      A filosofia de cérebro grande, como grug a vê, é escolher a reutilização a qualquer custo, exceto quando ela for absolutamente inevitável
      O problema dessa filosofia é que ela cristaliza as escolhas de design da primeira geração e torna a melhoria iterativa mais difícil
      É verdade que encontrar a simplicidade é difícil, mas a chave para encontrá-la é a melhoria iterativa
    • O que há de especialmente bom no texto do Grug é que ele é cheio de nuance e humildade. Não é arrogância
    • Na minha opinião, dá para medir pelo número de linhas de código
      Como os mínimos locais se movem conforme a complexidade do que está sendo construído, há espaço para várias visões bem inteligentes estarem corretas ao mesmo tempo
      Não é uma métrica perfeita, mas aponta na direção certa e funciona muito bem em escala logarítmica
  • Usei e expliquei inúmeras vezes o conceito da cerca de Chesterton, e é bom finalmente saber o nome dele
    Isso acontece com muita frequência ao trabalhar com desenvolvedores iniciantes. Quando veem “lixo legado velho”, a primeira reação deles é querer arrancar tudo ou jogar fora e refazer do zero
    Em alguns casos, deixá-los tentar por conta própria pode ser um aprendizado, mas é bom lembrar que as pessoas que vieram antes de nós não eram todas completas idiotas, e em geral havia um motivo para terem feito aquilo
    Às vezes é de fato um código antigo e bagunçado que precisa ser substituído, mas mesmo nesses casos ele muitas vezes contém um registro arduamente conquistado de todos os cantos e casos de borda que precisam ser entendidos e tratados para construir algo naquele domínio

  • A parte sobre testes é realmente boa. Bate exatamente com o que venho aprendendo ao longo de anos
    Testes de integração são o ponto adequado para encontrar bugs
    Mocks tendem a complicar demais as coisas e, embora eu ainda os use às vezes, evito usá-los de forma sistemática
    Testes unitários são frágeis demais diante de refatorações, enquanto testes de integração ajudam na refatoração

    • Um truque de que gosto é criar testes de integração que sigam o cenário da demonstração e executá-los logo antes da demo. Se possível, executo duas vezes
    • Discordo fortemente. Testes de integração funcionam muito bem até se chegar a certo tamanho ou complexidade, mas depois desse limite ficam realmente ruins
      Testes unitários são mais difíceis de escrever e manter, mas, quando falham, são muito mais fáceis de entender e depurar, então se sustentam muito melhor no longo prazo
      O pior tipo de teste é um teste de integração que depende secretamente do fato de outro teste de integração ter sido executado antes. Em 99% das vezes ele está certo, até que alguma mudança altera a ordem e ele quebra
    • A conclusão a que cheguei sobre testes é esta
      Se a equipe tem código simples, testes ajudam muito. Mas, se a equipe não tem código simples — e normalmente não tem —, é melhor gastar o tempo simplificando o código do que escrevendo testes
    • Acho que muita coisa depende do que você considera uma “unidade”
      Tomar uma única função folha como unidade muitas vezes é um nível baixo demais, mas um módulo maior com uma interface relativamente estável pode ser uma boa unidade produtiva para testar
  • “Complexidade é muito, muito ruim”
    “A melhor arma contra o demônio espiritual da complexidade é a palavra mágica ‘não’”
    “Triste, mas verdade: depois de aprender ‘sim’ e falhar, aprender a culpar outro grug é o conselho de carreira ideal”
    É a sabedoria complexa de grug

  • Isso também foi discutido na época
    The Grug Brained Developer - https://news.ycombinator.com/item?id=31840331 - junho de 2022, 374 comentários

    • Complexidade continua sendo muito ruim